From f6ed0ba4ece57f541d4dfc1f643e695a393c9654 Mon Sep 17 00:00:00 2001 From: Northern Man <19808920+NorthernMan54@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:36:19 -0500 Subject: [PATCH] Initial pass prior to regression --- contrib/acurite.c | 2068 ------------------- contrib/govee.c | 407 ---- contrib/interlogix.c | 239 --- contrib/lacrosse_tx141x.c | 331 --- contrib/oregon_scientific.c | 974 --------- include/data.h | 152 +- include/pulse_analyzer.h | 4 +- include/pulse_data.h | 65 +- include/r_private.h | 2 +- include/rtl_433.h | 158 +- include/rtl_433_devices.h | 508 ++--- src/rtl_433/abuf.c | 2 +- src/rtl_433/data.c | 7 +- src/rtl_433/decoder_util.c | 18 +- src/rtl_433/devices/acurite.c | 37 +- src/rtl_433/devices/ambientweather_wh31e.c | 34 +- src/rtl_433/devices/ant_antplus.c | 6 +- src/rtl_433/devices/archos_tbh.c | 2 +- src/rtl_433/devices/auriol_4ld5661.c | 25 +- src/rtl_433/devices/blyss.c | 13 +- src/rtl_433/devices/bresser_5in1.c | 3 +- src/rtl_433/devices/bresser_6in1.c | 21 +- src/rtl_433/devices/bresser_7in1.c | 143 +- src/rtl_433/devices/bresser_leakage.c | 158 ++ src/rtl_433/devices/bresser_lightning.c | 136 ++ src/rtl_433/devices/burnhardbbq.c | 2 +- src/rtl_433/devices/chuango.c | 21 +- src/rtl_433/devices/cotech_36_7959.c | 7 +- src/rtl_433/devices/digitech_xc0324.c | 4 +- src/rtl_433/devices/dsc.c | 10 +- src/rtl_433/devices/ecodhome.c | 2 +- src/rtl_433/devices/efth800.c | 7 +- src/rtl_433/devices/elro_db286a.c | 2 +- src/rtl_433/devices/emax.c | 6 +- src/rtl_433/devices/emos_e6016.c | 18 +- src/rtl_433/devices/fineoffset.c | 4 +- src/rtl_433/devices/fineoffset_wh1050.c | 234 ++- src/rtl_433/devices/fineoffset_wh1080.c | 2 +- src/rtl_433/devices/fineoffset_ws90.c | 174 ++ src/rtl_433/devices/flex.c | 5 + src/rtl_433/devices/flowis.c | 28 +- src/rtl_433/devices/funkbus.c | 8 +- src/rtl_433/devices/generic_motion.c | 18 +- src/rtl_433/devices/govee.c | 37 +- src/rtl_433/devices/hcs200.c | 6 +- src/rtl_433/devices/holman_ws5029.c | 269 ++- src/rtl_433/devices/honeywell_cm921.c | 42 +- src/rtl_433/devices/ikea_sparsnas.c | 2 +- src/rtl_433/devices/inkbird_ith20r.c | 10 +- src/rtl_433/devices/interlogix.c | 14 +- src/rtl_433/devices/intertechno.c | 2 +- src/rtl_433/devices/lacrosse_tx141x.c | 25 +- src/rtl_433/devices/lacrosse_tx31u.c | 8 +- src/rtl_433/devices/maverick_et73x.c | 2 +- src/rtl_433/devices/neptune_r900.c | 4 +- src/rtl_433/devices/norgo.c | 6 +- src/rtl_433/devices/oil_watchman_advanced.c | 31 +- src/rtl_433/devices/oregon_scientific.c | 236 +-- src/rtl_433/devices/regency_fan.c | 10 +- src/rtl_433/devices/revolt_nc5462.c | 3 + src/rtl_433/devices/schou_72543_rain.c | 122 ++ src/rtl_433/devices/schraeder.c | 18 +- src/rtl_433/devices/secplus_v1.c | 8 +- src/rtl_433/devices/simplisafe.c | 33 +- src/rtl_433/devices/smoke_gs558.c | 7 +- src/rtl_433/devices/somfy_iohc.c | 2 + src/rtl_433/devices/somfy_rts.c | 2 +- src/rtl_433/devices/steelmate.c | 13 +- src/rtl_433/devices/tfa_marbella.c | 9 +- src/rtl_433/devices/thermopro_tp11.c | 16 +- src/rtl_433/devices/thermopro_tp12.c | 49 +- src/rtl_433/devices/thermopro_tx2.c | 8 +- src/rtl_433/devices/thermopro_tx2c.c | 115 ++ src/rtl_433/devices/tpms_abarth124.c | 14 +- src/rtl_433/devices/tpms_ave.c | 7 +- src/rtl_433/devices/tpms_citroen.c | 16 +- src/rtl_433/devices/tpms_eezrv.c | 121 +- src/rtl_433/devices/tpms_elantra2012.c | 23 +- src/rtl_433/devices/tpms_ford.c | 20 +- src/rtl_433/devices/tpms_hyundai_vdo.c | 7 +- src/rtl_433/devices/tpms_jansite.c | 12 +- src/rtl_433/devices/tpms_jansite_solar.c | 12 +- src/rtl_433/devices/tpms_kia.c | 21 +- src/rtl_433/devices/tpms_nissan.c | 115 ++ src/rtl_433/devices/tpms_pmv107j.c | 2 +- src/rtl_433/devices/tpms_porsche.c | 2 +- src/rtl_433/devices/tpms_renault.c | 22 +- src/rtl_433/devices/tpms_renault_0435r.c | 12 +- src/rtl_433/devices/tpms_toyota.c | 7 +- src/rtl_433/devices/tpms_truck.c | 2 +- src/rtl_433/devices/tpms_tyreguard400.c | 8 +- src/rtl_433/devices/ttx201.c | 87 +- src/rtl_433/devices/visonic_powercode.c | 10 +- src/rtl_433/devices/wec2103.c | 62 +- src/rtl_433/devices/ws2032.c | 2 +- src/rtl_433/devices/x10_sec.c | 8 +- src/rtl_433/list.c | 6 +- src/rtl_433/output_log.c | 2 +- src/rtl_433/pulse_analyzer.c | 115 +- src/rtl_433/pulse_data.c | 13 + src/rtl_433/pulse_slicer.c | 19 +- src/rtl_433/r_api.c | 660 +++--- src/rtl_433/r_util.c | 14 +- src/signalDecoder.cpp | 496 ++--- tools/decoder.fragment | 229 +- tools/rtl_433_devices.fragment | 11 +- 106 files changed, 3276 insertions(+), 6055 deletions(-) delete mode 100644 contrib/acurite.c delete mode 100644 contrib/govee.c delete mode 100644 contrib/interlogix.c delete mode 100644 contrib/lacrosse_tx141x.c delete mode 100644 contrib/oregon_scientific.c create mode 100644 src/rtl_433/devices/bresser_leakage.c create mode 100644 src/rtl_433/devices/bresser_lightning.c create mode 100644 src/rtl_433/devices/fineoffset_ws90.c create mode 100644 src/rtl_433/devices/schou_72543_rain.c create mode 100644 src/rtl_433/devices/thermopro_tx2c.c create mode 100644 src/rtl_433/devices/tpms_nissan.c diff --git a/contrib/acurite.c b/contrib/acurite.c deleted file mode 100644 index f71c5128..00000000 --- a/contrib/acurite.c +++ /dev/null @@ -1,2068 +0,0 @@ -/** @file - Acurite weather stations and temperature / humidity sensors. - - Copyright (c) 2015, Jens Jenson, Helge Weissig, David Ray Thompson, Robert Terzi - - 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. - -Acurite weather stations and temperature / humidity sensors. - -Devices decoded: -- Acurite Iris (5-n-1) weather station, Model; VN1TXC, 06004RM -- Acurite 5-n-1 pro weather sensor, Model: 06014RM -- Acurite Atlas (7-n-1) weather station -- Acurite Notos (3-n-1) weather station -- Acurite 896 Rain gauge, Model: 00896 -- Acurite 592TXR / 06002RM / 6044m Tower sensor (temperature and humidity) - (Note: Some newer sensors share the 592TXR coding for compatibility. -- Acurite 609TXC "TH" temperature and humidity sensor (609A1TX) -- Acurite 986 Refrigerator / Freezer Thermometer -- Acurite 515 Refrigerator / Freezer Thermometer -- Acurite 606TX temperature sensor -- Acurite 6045M Lightning Detector -- Acurite 00275rm and 00276rm temp. and humidity with optional probe. -- Acurite 1190/1192 leak/water detector -*/ - -#include "decoder.h" - -#define ACURITE_515_BITLEN 50 -#define ACURITE_TXR_BITLEN 56 -#define ACURITE_5N1_BITLEN 64 -#define ACURITE_6045_BITLEN 72 -#define ACURITE_ATLAS_BITLEN 80 - -#define ACURITE_515_BYTELEN 6 -#define ACURITE_TXR_BYTELEN 7 -#define ACURITE_1190_BYTELEN 7 -#define ACURITE_3N1_BYTELEN 8 -#define ACURITE_5N1_BYTELEN 8 -#define ACURITE_899_BYTELEN 8 -#define ACURITE_ATLAS_BYTELEN 8 -#define ACURITE_6045_BYTELEN 9 -#define ACURITE_ATLAS_LTNG_BYTELEN 10 - - -// ** Acurite known message types -#define ACURITE_MSGTYPE_1190_DETECTOR 0x01 - -#define ACURITE_MSGTYPE_TOWER_SENSOR 0x04 - -#define ACURITE_MSGTYPE_ATLAS_WNDSPD_TEMP_HUM 0x05 -#define ACURITE_MSGTYPE_ATLAS_WNDSPD_RAIN 0x06 -#define ACURITE_MSGTYPE_ATLAS_WNDSPD_UV_LUX 0x07 - -#define ACURITE_MSGTYPE_515_REFRIGERATOR 0x08 -#define ACURITE_MSGTYPE_515_FREEZER 0x09 - -#define ACURITE_MSGTYPE_3N1_WINDSPEED_TEMP_HUMIDITY 0x20 - -#define ACURITE_MSGTYPE_ATLAS_WNDSPD_TEMP_HUM_LTNG 0x25 -#define ACURITE_MSGTYPE_ATLAS_WNDSPD_RAIN_LTNG 0x26 -#define ACURITE_MSGTYPE_ATLAS_WNDSPD_UV_LUX_LTNG 0x27 - -#define ACURITE_MSGTYPE_6045M 0x2f -#define ACURITE_MSGTYPE_899_RAINFALL 0x30 -#define ACURITE_MSGTYPE_5N1_WINDSPEED_WINDDIR_RAINFALL 0x31 -#define ACURITE_MSGTYPE_5N1_WINDSPEED_TEMP_HUMIDITY 0x38 - - - -// Acurite 5n1 Wind direction values. -// There are seem to be conflicting decodings. -// It is possible there there are different versions -// of the 5n1 station that report differently. -// -// The original implementation used by the 5n1 device type -// here seems to have a straight linear/circular mapping. -// -// The newer 5n1 mapping seems to just jump around with no clear -// meaning, but does map to the values sent by Acurite's -// only Acu-Link Internet Bridge and physical console 1512. -// This is may be a modified/non-standard Gray Code. - -// Mapping 5n1 raw RF wind direction values to aculink's values -// RF, AcuLink -// 0, 6, NW, 315.0 -// 1, 8, WSW, 247.5 -// 2, 2, WNW, 292.5 -// 3, 0, W, 270.0 -// 4, 4, NNW, 337.5 -// 5, A, SW, 225.0 -// 6, 5, N, 0.0 -// 7, E, SSW, 202.5 -// 8, 1, ENE, 67.5 -// 9, F, SE, 135.0 -// A, 9, E, 90.0 -// B, B, ESE, 112.5 -// C, 3, NE, 45.0 -// D, D, SSE, 157.0 -// E, 7, NNE, 22.5 -// F, C, S, 180.0 - -// From draythomp/Desert-home-rtl_433 -// matches acu-link internet bridge values -// The mapping isn't circular, it jumps around. -// units are 22.5 deg -int const acurite_5n1_winddirections[] = { - 14, // 0 - NW - 11, // 1 - WSW - 13, // 2 - WNW - 12, // 3 - W - 15, // 4 - NNW - 10, // 5 - SW - 0, // 6 - N - 9, // 7 - SSW - 3, // 8 - ENE - 6, // 9 - SE - 4, // a - E - 5, // b - ESE - 2, // c - NE - 7, // d - SSE - 1, // e - NNE - 8, // f - S -}; - -// The high 2 bits of byte zero are the channel (bits 7,6) -// 00 = C -// 10 = B -// 11 = A -static char const *acurite_getChannel(uint8_t byte) -{ - static char const *const channel_strs[] = {"C", "E", "B", "A"}; // 'E' stands for error - - int channel = (byte & 0xC0) >> 6; - return channel_strs[channel]; -} - -// Add exception and raw message bytes to message to enable -// later analysis of unexpected/possbily undecoded data -static void data_append_exception(data_t* data, int exception, uint8_t* bb, int browlen) -{ - char raw_str[31], *rawp; - - rawp = (char *)raw_str; - for (int i=0; i < browlen; i++) { - sprintf(rawp,"%02x",bb[i]); - rawp += 2; - } - *rawp = '\0'; - - /* clang-format off */ - data = data_append(data, - "exception", "data_exception", DATA_INT, exception, - "raw_msg", "raw_message", DATA_STRING, raw_str, - NULL); - /* clang-format on */ - -} - - -/** -Acurite 896 rain gauge - -*/ -static int acurite_rain_896_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - uint8_t *b = bitbuffer->bb[0]; - int id; - float total_rain; - data_t *data; - - // This needs more validation to positively identify correct sensor type, but it basically works if message is really from acurite raingauge and it doesn't have any errors - if (bitbuffer->bits_per_row[0] < 24) - return DECODE_ABORT_LENGTH; - - // The nominal repeat count is 16, require a minimum of 12 rows - if (bitbuffer->num_rows < 12) - return DECODE_ABORT_EARLY; // likely Oregon V1, not AcuRite - - if ((b[0] == 0) || (b[1] == 0) || (b[2] == 0) || (b[3] != 0) || (b[4] != 0)) - return DECODE_ABORT_EARLY; - - id = b[0]; - total_rain = ((b[1] & 0xf) << 8) | b[2]; - total_rain *= 0.5; // Sensor reports number of bucket tips. Each bucket tip is .5mm - - decoder_logf(decoder, 2, __func__, "Total Rain is %2.1fmm", total_rain); - decoder_log_bitrow(decoder, 2, __func__, b, bitbuffer->bits_per_row[0], "Raw Message "); - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-Rain", - "id", "", DATA_INT, id, - "rain_mm", "Total Rain", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, total_rain, - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - return 1; -} - -/** -Acurite 609 Temperature and Humidity Sensor. - -5 byte messages: - - II ST TT HH CC - II - ID byte, changes at each power up - S - Status bitmask, normally 0x2, - 0xa - battery low (bit 0x80) - TTT - Temp in Celsius * 10, 12 bit with complement. - HH - Humidity - CC - Checksum - -@todo - see if the 3rd nybble is battery/status -*/ -static int acurite_th_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - uint8_t *bb = NULL; - int cksum, battery_low, valid = 0; - float tempc; - uint8_t humidity, id, status; - data_t *data; - int result = 0; - - for (uint16_t brow = 0; brow < bitbuffer->num_rows; ++brow) { - if (bitbuffer->bits_per_row[brow] != 40) { - result = DECODE_ABORT_LENGTH; - continue; // DECODE_ABORT_LENGTH - } - - bb = bitbuffer->bb[brow]; - - cksum = (bb[0] + bb[1] + bb[2] + bb[3]); - - if (cksum == 0 || ((cksum & 0xff) != bb[4])) { - result = DECODE_FAIL_MIC; - continue; // DECODE_FAIL_MIC - } - - // Temperature in Celsius is encoded as a 12 bit integer value - // multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2) - // negative values are recovered by sign extend from int16_t. - int temp_raw = (int16_t)(((bb[1] & 0x0f) << 12) | (bb[2] << 4)); - tempc = (temp_raw >> 4) * 0.1f; - id = bb[0]; - status = (bb[1] & 0xf0) >> 4; - battery_low = status & 0x8; - humidity = bb[3]; - - if (humidity > 100) { - decoder_logf(decoder, 1, __func__, "609txc 0x%04X: invalid humidity: %d %%rH", - id, humidity); - return DECODE_FAIL_SANITY; - } - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-609TXC", - "id", "", DATA_INT, id, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, tempc, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - "status", "", DATA_INT, status, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - valid++; - } - - if (valid) - return 1; - - // Only returns the latest result, but better than nothing. - return result; -} - -/** -Acurite 06045m Lightning Sensor decoding. - -Specs: -- lightning strike count -- estimated distance to front of storm, 1 to 25 miles / 1.6 to 40 km -- Temperature -40 to 158 F / -40 to 70 C -- Humidity 1 - 99% RH - -Status Information sent per 06047M/01021 display -- (RF) interference (preventing lightning detection) -- low battery - -Message format: - -Somewhat similar to 592TXR and 5-n-1 weather stations. -Same pulse characteristics. checksum, and parity checking on data bytes. - - - Byte 0 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 Byte 8 - CCIIIIII IIIIIIII pB101111 pHHHHHHH pA?TTTTT pTTTTTTT pLLLLLLL pLRDDDDD KKKKKKKK - -- C = Channel (2 bits) -- I = Sensor ID (14 bit Static ID) -- p = parity check bit -- B = Battery OK (cleared for low) -- H = Humidity (7 bits) -- A = Active mode lightning detection (cleared for standby mode) -- T = Temperature (12 bits) -- L = Lightning strike count (8 bits) -- D = Lightning distance (5 bits) -- K = Checksum (8 bits) - -Byte 0 - channel/ID -- bitmask CCII IIII -- 0xC0: channel (A: 0xC, B: 0x8, C: 00) -- 0x3F: most significant 6 bits of Sensor ID - (14 bits, same as Acurite Tower sensor family) - -Byte 1 - ID all 8 bits, no parity. -- 0xFF = least significant 8 bits of Sensor ID - -Byte 2 - Battery and Message type -- Bitmask PBMMMMMM -- 0x80 = Parity -- 0x40 = 1 is battery OK, 0 is battery low -- 0x3f = Message type 0x2f for 06045M lightning detector - -Byte 3 - Humidity -- 0x80 - even parity -- 0x7f - humidity - -Byte 4 - Status (2 bits) and Temperature MSB (5 bits) -- Bitmask PA?TTTTT (P = Parity, A = Active, T = Temperature) -- 0x80 - even parity -- 0x40 - 1 is Active lightning detection Mode, 0 is standby -- 0x20 - TBD: always off? -- 0x1F - Temperature most significant 5 bits - -Byte 5 - Temperature LSB (7 bits, 8th is parity) -- 0x80 - even parity -- 0x7F - Temperature least significant 7 bits - -Byte 6 - Lightning Strike count (7 of 8 bit, 8th is parity) -- 0x80 - even parity -- 0x7F - strike count (upper 7 bits) wraps at 255 -> 0 - - -Byte 7 - Edge of Storm Distance Approximation & other bits -- Bits PLRDDDDD (P = Parity, S = Status, D = Distance -- 0x80 - even parity -- 0x40 - LSB of 8 bit strike counter -- 0x20 - RFI (radio frequency interference) -- 0x1F - distance to edge of storm - value 0x1f is possible invalid value indication (value at power up) - @todo determine mapping function/table. - - -Byte 8 - checksum. 8 bits, no parity. - -Data fields in rtl_433 messages: -- active (vs standby) lightning detection mode - When active: - the AS39335 is in active scanning mode - 6045M will transmit every 8 seconds instead of every 24. - -- RFI - radio frequency interference detected - The AS3935 uses broad RFI for detection - Somewhat correlates with the yellow LED on the sensor, but stays set longer - Short periods of RFI appears to be somewhat normal - long periods of RFI on indicates interference, relocate sensor until - yellow LED is no longer on solid - -- strike_count - count of detection events, 8 bits - counts up to 255, wraps around to 0 - non-volatile (doesn't reset at power up) - -- storm_distance - statistically estimated distance to edge of storm - See AS3935 documentation - sensor will make calculate a distance estimate with each strike event - 0x1f (31) is invalid/undefined value, used at power-up to indicate invalid - Only 5 bits available, needs to cover range of 25 miles/40 KM per spec. - Units unknown, data needed from people with Acurite consoles - -- exception - additional analysis of message maybe needed - Suggest reporting raw_msg for further examination. - bits that were invariant (for me) have changed. - -Notes: - -2020-08-29 - changed temperature decoding, was 2.0 F too low vs. Acurite Access - -@todo - storm_distance conversion to miles/KM (should match Acurite consoles) - -*/ -static int acurite_6045_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row) -{ - float tempf; - uint8_t humidity; - char raw_str[31], *rawp; - uint16_t sensor_id; - uint8_t strike_count, strike_distance; - int battery_low, active, rfi_detect; - int exception = 0; - data_t *data; - - int browlen = (bitbuffer->bits_per_row[row] + 7) / 8; - uint8_t *bb = bitbuffer->bb[row]; - - char const *channel_str = acurite_getChannel(bb[0]); // same as TXR - - // Tower sensor ID is the last 14 bits of byte 0 and 1 - // CCII IIII | IIII IIII - sensor_id = ((bb[0] & 0x3f) << 8) | bb[1]; // same as TXR - battery_low = (bb[2] & 0x40) == 0; - - humidity = (bb[3] & 0x7f); // 1-99 %rH, same as TXR - if (humidity > 100) { - decoder_logf(decoder, 1, __func__, "6045m 0x%04X Ch %s : invalid humidity: %d %%rH", - sensor_id, channel_str, humidity); - return DECODE_FAIL_SANITY; - } - - active = (bb[4] & 0x40) == 0x40; // Sensor is actively listening for strikes - //message_type = bb[2] & 0x3f; - - // 12 bits of temperature after removing parity and status bits. - // Message native format appears to be in 1/10 of a degree Fahrenheit - // Device Specification: -40 to 158 F / -40 to 70 C - // Available range given 12 bits with +1480 offset: -148.0 F to +261.5 F - int temp_raw = ((bb[4] & 0x1F) << 7) | (bb[5] & 0x7F); - tempf = (temp_raw - 1480) * 0.1f; - - if (tempf < -40.0 || tempf > 158.0) { - decoder_logf(decoder, 1, __func__, "6045m 0x%04X Ch %s, invalid temperature: %0.1f F", - sensor_id, channel_str, tempf); - return DECODE_FAIL_SANITY; - } - - // flag if bits 13/14 of temperature are ever non-zero so - // they can be investigated - if (temp_raw & 0x3000) - exception++; - - // Strike count is 8 bits, LSB in following byte - strike_count = ((bb[6] & 0x7f) << 1) | ((bb[7] & 0x40) >> 6); - strike_distance = bb[7] & 0x1f; - rfi_detect = (bb[7] & 0x20) == 0x20; - - - /* - * 2018-04-21 rct - There are still a number of unknown bits in the - * message that need to be figured out. Add the raw message hex to - * to the structured data output to allow future analysis without - * having to enable debug for long running rtl_433 processes. - */ - rawp = (char *)raw_str; - for (int i=0; i < MIN(browlen, 15); i++) { - sprintf(rawp,"%02x",bb[i]); - rawp += 2; - } - *rawp = '\0'; - - - // Flag whether this message might need further analysis - if ((bb[4] & 0x20) != 0) // unknown status bits, always off - exception++; - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-6045M", - "id", NULL, DATA_INT, sensor_id, - "channel", NULL, DATA_STRING, channel_str, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_F", "temperature", DATA_FORMAT, "%.1f F", DATA_DOUBLE, tempf, - "humidity", "humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - "strike_count", "strike_count", DATA_INT, strike_count, - "storm_dist", "storm_distance", DATA_INT, strike_distance, - "active", "active_mode", DATA_INT, active, - "rfi", "rfi_detect", DATA_INT, rfi_detect, - "exception", "data_exception", DATA_INT, exception, - "raw_msg", "raw_message", DATA_STRING, raw_str, - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - - return 1; // If we got here 1 valid message was output -} - -/** -Acurite 899 Rain Gauge decoder - -*/ -static int acurite_899_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t *bb) -{ - (void)bitbuffer; - // MIC (checkum, parity) validated in calling function - - uint16_t sensor_id = ((bb[0] & 0x3f) << 8) | bb[1]; // - int battery_low = (bb[2] & 0x40) == 0; - - /* - @todo bug? channel output isn't consistent with the rest of he Acurite - devices in this family, should output ('A', 'B', or 'C') - Currently outputing 00 = A, 01 = B, 10 = C - Leaving as is to maintain compatibility for now - */ - - int channel = bb[0] >> 6; - // @todo replace the above with this: - // char const* channel_str = acurite_getChannel(bb[0]); - - - /* - Rain counter - one tip is 0.01 inch, i.e. 0.254mm - Note: Device native unit arguably Imperial - but this is being converted to metric here, so -C native won't work - Leaving as is to maintain compatibility - */ - int raincounter = ((bb[5] & 0x7f) << 7) | (bb[6] & 0x7f); - - /* clang-format off */ - data_t *data; - data = data_make( - "model", "", DATA_STRING, "Acurite-Rain899", - "id", "", DATA_INT, sensor_id, - "channel", "", DATA_INT, channel, - // "channel", NULL, DATA_STRING, channel_str, - "battery_ok", "Battery", DATA_INT, !battery_low, - "rain_mm", "Rainfall Accumulation", DATA_FORMAT, "%.2f mm", DATA_DOUBLE, raincounter * 0.254, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - - return 1; // if we got here, 1 message was output - -} - -/** -Acurite 3n1 Weather Station decoder - -*/ -static int acurite_3n1_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t *bb) -{ - // MIC (checkum, parity) validated in calling function - (void)bitbuffer; - - char const* channel_str = acurite_getChannel(bb[0]); - - // 3n1 sensor ID is 14 bits - uint16_t sensor_id = ((bb[0] & 0x3f) << 8) | bb[1]; - uint8_t message_type = bb[2] & 0x3f; - - if (*channel_str == 'E') { - decoder_logf(decoder, 1, __func__, - "bad channel Ch %s, msg type 0x%02x", - channel_str, message_type); - return DECODE_FAIL_SANITY; - } - - /* - @todo bug, 3n1 data format includes sequence_num - which was copied from 5n1, but existing code 3n1 uses - 14 bits for ID. so these bits are used twice. - - Leaving for compatibility, but probaby sequence_num - doesn't exist and should be deleted. If the 3n1 did use - a sequence number, the ID would change on each output. - */ - uint8_t sequence_num = (bb[0] & 0x30) >> 4; - - int battery_low = (bb[2] & 0x40) == 0; - uint8_t humidity = (bb[3] & 0x7f); // 1-99 %rH - if (humidity > 100) { - decoder_logf(decoder, 1, __func__, "3n1 0x%04X Ch %s : invalid humidity: %d %%rH", - sensor_id, channel_str, humidity); - return DECODE_FAIL_SANITY; - } - - // note the 3n1 seems to have one more high bit than 5n1 - // Spec: -40 to 158 F - int temp_raw = (bb[4] & 0x1F) << 7 | (bb[5] & 0x7F); - float tempf = (temp_raw - 1480) * 0.1f; // regression yields (rawtemp-1480)*0.1 - - if (tempf < -40.0 || tempf > 158.0) { - decoder_logf(decoder, 1, __func__, "3n1 0x%04X Ch %s, invalid temperature: %0.1f F", - sensor_id, channel_str, tempf); - return DECODE_FAIL_SANITY; - } - - - /* - @todo bug from original decoder - This can't be a float, must be uint8 - leaving for compatibility - */ - float wind_speed_mph = bb[6] & 0x7f; // seems to be plain MPH - - /* clang-format off */ - data_t *data; - data = data_make( - "model", "", DATA_STRING, "Acurite-3n1", - "message_type", NULL, DATA_INT, message_type, - "id", NULL, DATA_FORMAT, "0x%02X", DATA_INT, sensor_id, - "channel", NULL, DATA_STRING, channel_str, - "sequence_num", NULL, DATA_INT, sequence_num, - "battery_ok", "Battery", DATA_INT, !battery_low, - "wind_avg_mi_h", "wind_speed", DATA_FORMAT, "%.1f mi/h", DATA_DOUBLE, wind_speed_mph, - "temperature_F", "temperature", DATA_FORMAT, "%.1f F", DATA_DOUBLE, tempf, - "humidity", NULL, DATA_FORMAT, "%u %%", DATA_INT, humidity, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - - return 1; // If we got here 1 valid message was output -} - - -/** -Acurite 5n1 Weather Station decoder - -XXX todo docs - -*/ -static int acurite_5n1_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t* bb) -{ - // MIC (checkum, parity) validated in calling function - (void)bitbuffer; - - char const* channel_str = acurite_getChannel(bb[0]); - uint16_t sensor_id = ((bb[0] & 0x0f) << 8) | bb[1]; - uint8_t sequence_num = (bb[0] & 0x30) >> 4; - int battery_low = (bb[2] & 0x40) == 0; - uint8_t message_type = bb[2] & 0x3f; - - // Wind raw number is cup rotations per 4 seconds - // 8 bits gives range of 0 - 212 KPH - // http://www.wxforum.net/index.php?topic=27244.0 (found from weewx driver) - int wind_speed_raw = ((bb[3] & 0x1F) << 3)| ((bb[4] & 0x70) >> 4); - float wind_speed_kph = 0; - if (wind_speed_raw > 0) { - wind_speed_kph = wind_speed_raw * 0.8278 + 1.0; - } - - if (message_type == ACURITE_MSGTYPE_5N1_WINDSPEED_WINDDIR_RAINFALL) { - // Wind speed, wind direction, and rain fall - float wind_dir = acurite_5n1_winddirections[bb[4] & 0x0f] * 22.5f; - - // range: 0 to 99.99 in, 0.01 inch increments, accumulated - int raincounter = ((bb[5] & 0x7f) << 7) | (bb[6] & 0x7F); - - /* clang-format off */ - data_t *data; - data = data_make( - "model", "", DATA_STRING, "Acurite-5n1", - "message_type", NULL, DATA_INT, message_type, - "id", NULL, DATA_INT, sensor_id, - "channel", NULL, DATA_STRING, channel_str, - "sequence_num", NULL, DATA_INT, sequence_num, - "battery_ok", "Battery", DATA_INT, !battery_low, - "wind_avg_km_h", "wind_speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, wind_speed_kph, - "wind_dir_deg", NULL, DATA_FORMAT, "%.1f", DATA_DOUBLE, wind_dir, - "rain_in", "Rainfall Accumulation", DATA_FORMAT, "%.2f in", DATA_DOUBLE, raincounter * 0.01f, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - } - else if (message_type == ACURITE_MSGTYPE_5N1_WINDSPEED_TEMP_HUMIDITY) { - // Wind speed, temperature and humidity - - // range -40 to 158 F - int temp_raw = (bb[4] & 0x0F) << 7 | (bb[5] & 0x7F); - float tempf = (temp_raw - 400) * 0.1f; - - if (tempf > 158.0) { - decoder_logf(decoder, 1, __func__, "5n1 0x%04X Ch %s, invalid temperature: %0.1f F", - sensor_id, channel_str, tempf); - return DECODE_FAIL_SANITY; - } - - uint8_t humidity = (bb[6] & 0x7f); // 1-99 %rH - if (humidity > 100) { - decoder_logf(decoder, 1, __func__, "5n1 0x%04X Ch %s : invalid humidity: %d %%rH", - sensor_id, channel_str, humidity); - return DECODE_FAIL_SANITY; - } - - - /* clang-format off */ - data_t *data; - data = data_make( - "model", "", DATA_STRING, "Acurite-5n1", - "message_type", NULL, DATA_INT, message_type, - "id", NULL, DATA_INT, sensor_id, - "channel", NULL, DATA_STRING, channel_str, - "sequence_num", NULL, DATA_INT, sequence_num, - "battery_ok", "Battery", DATA_INT, !battery_low, - "wind_avg_km_h", "wind_speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, wind_speed_kph, - "temperature_F", "temperature", DATA_FORMAT, "%.1f F", DATA_DOUBLE, tempf, - "humidity", NULL, DATA_FORMAT, "%u %%", DATA_INT, humidity, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - } else { - decoder_logf(decoder, 1, __func__, "unknown message type 0x02%x", message_type); - return DECODE_FAIL_SANITY; - } - - return 1; // If we got here 1 valid message was output -} - - -/** -Acurite Atlas weather and lightning sensor. - -| Reading | Operating Range | Reading Frequency | Accuracy | -| --- | --- | --- | --- | -| Temperature Range | -40 to 158°F (-40 to 70°C) | 30 seconds | ± 1°F | -| Humidity Range | 1-100% RH | 30 seconds | ± 2% RH | -| Wind Speed | 0-160 mph (0-257 km/h) | 10 seconds | ± 1 mph ≤ 10 mph, ± 10% > 10 mph | -| Wind Direction | 360° | 30 seconds | ± 3° | -| Rain | .01 inch intervals (0.254 mm) | 30 seconds | ± 5% | -| UV Index | 0 to 15 index | 30 seconds | ± 1 | -| Light Intensity | to 120,000 Lumens | 30 seconds | n/a | -| Lightning | Up to 25 miles away (40 km) | 10 seconds | n/a | - -The Atlas reports direction with an AS5600 hall effect sensor, it has 12-bit resolution according to the spec sheet. https://ams.com/as5600 - -Acurite Atlas Message Type Format: - -Message Type 0x25 (Wind Speed, Temperature, Relative Humidity, ???) - - Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 Byte 8 Byte 9 Byte 10 - cc??ssdd dddddddd pb011011 pWWWWWWW pWTTTTTT pTTTTTTT pHHHHHHH pCCCCCCC pCCDDDDD kkkkkkkkk - -Note: 13 bits for Temp is too much, should only be 11 bits. - -Message Type 0x26 (Wind Speed, Wind Vector, Rain Counter, ???) - - Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 Byte 8 Byte 9 Byte 10 - cc??ssdd dddddddd pb011100 pWWWWWWW pW?VVVVV pVVVVVRR pRRRRRRR pCCCCCCC pCCDDDDD kkkkkkkkk - - CHANNEL:2b xx ~SEQ:2d ~DEVICE:10d xx ~TYPE:6h SPEED:x~7bx~1b DIR:x~5bx~5bxx x~7b x~7b x~7b CHK:8h - -Note: 10 bits for Vector is too much, should only be 9 bits. -Note: 7 bits for Rain not enough, should reasonably be 10 bits. - -Message Type 0x27 (Wind Speed, UV and Lux data) - - Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 Byte 8 Byte 9 Byte 10 - cc??ssdd dddddddd pb011101 pWWWWWWW pW??UUUU pLLLLLLL pLLLLLLL pCCCCCCC pCCDDDDD kkkkkkkkk - -Note: 6 bits for UV is too much, should only be 4 bits. -JRH - Definitely only 4 bits, seeing the occasional value of 32 or 34. No idea what the 2 bits between - wind speed and UV are. - - CHANNEL:2b xx ~SEQ:2d ~DEVICE:10d xx ~TYPE:6h SPEED:x~7bx~1b UV:~6d LUX:x~7bx~7b x~7b x~7b CHK:8h - -Lux needs to multiplied by 10. - -- b = bATTERY -- c = cHANNEL -- d = dEVICE -- k = CHECkSUM -- p = pARITY -- s = sEQUENCE -- ? = uNKNOWN - -- H = relative Humidity (percent) -- R = Rain (0.01 inch bucket tip count) -- T = Temperature (Fahrenheit. Subtract 400 then divide by 10.) -- V = wind Vector (degrees decimal) -- W = Wind speed (miles per hour) -- U = UV Index -- L = Lux -- C = lightning strike Count -- D = lightning Distance (miles) - -*/ -static int acurite_atlas_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row) -{ - uint8_t humidity, sequence_num, message_type; - char raw_str[31], *rawp; - uint16_t sensor_id; - int raincounter, battery_low; - int exception = 0; - float tempf, wind_dir, wind_speed_mph; - data_t *data; - - int browlen = (bitbuffer->bits_per_row[row] + 7) / 8; - uint8_t *bb = bitbuffer->bb[row]; - - message_type = bb[2] & 0x3f; - sensor_id = ((bb[0] & 0x03) << 8) | bb[1]; - char const *channel_str = acurite_getChannel(bb[0]); - - // There are still a few unknown/unused bits in the message that - // message that could possibly hold some data. Add the raw message hex to - // to the structured data output to allow future analysis without - // having to enable debug for long running rtl_433 processes. - rawp = (char *)raw_str; - for (int i=0; i < MIN(browlen, 15); i++) { - sprintf(rawp,"%02x",bb[i]); - rawp += 2; - } - *rawp = '\0'; - - // The sensor sends the same data three times, each of these have - // an indicator of which one of the three it is. This means the - // checksum and first byte will be different for each one. - // The bits 4,5 of byte 0 indicate which copy - // xxxx 00 xx = first copy - // xxxx 01 xx = second copy - // xxxx 10 xx = third copy - sequence_num = (bb[0] & 0x0c) >> 2; - // Battery status is the 7th bit 0x40. 1 = normal, 0 = low - battery_low = (bb[2] & 0x40) == 0; - - // Wind speed is 8-bits raw MPH - // Spec is 0-200 MPH - wind_speed_mph = ((bb[3] & 0x7F) << 1) | ((bb[4] & 0x40) >> 6); - - if (wind_speed_mph > 200) { - decoder_logf(decoder, 1, __func__, "Atlas 0x%04X Ch %s, invalid wind spped: %.1f MPH", - sensor_id, channel_str, wind_speed_mph); - return DECODE_FAIL_SANITY; - } - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-Atlas", - "id", NULL, DATA_INT, sensor_id, - "channel", NULL, DATA_STRING, channel_str, - "sequence_num", NULL, DATA_INT, sequence_num, - "battery_ok", "Battery", DATA_INT, !battery_low, - "message_type", NULL, DATA_INT, message_type, - "wind_avg_mi_h", "Wind Speed", DATA_FORMAT, "%.1f mi/h", DATA_DOUBLE, wind_speed_mph, - NULL); - /* clang-format on */ - - if (message_type == ACURITE_MSGTYPE_ATLAS_WNDSPD_TEMP_HUM || - message_type == ACURITE_MSGTYPE_ATLAS_WNDSPD_TEMP_HUM_LTNG) { - // Wind speed, temperature and humidity - - // Spec: temperature range -40 to 158 F - // There seem to be 13 bits for temperature but only 11 needed. - // Decode as 11 bits, flag exception if the other two bits are ever - // non-zero so they can be investigated. - int temp_raw = (bb[4] & 0x0F) << 7 | (bb[5] & 0x7F); - if ((bb[4] & 0x30) != 0) - exception++; - - tempf = (temp_raw - 400) * 0.1; - if (tempf > 158.0) { - decoder_logf(decoder, 1, __func__, "Atlas 0x%04X Ch %s, invalid temperature: %0.1f F", - sensor_id, channel_str, tempf); - return DECODE_FAIL_SANITY; - } - - - // Fail sanity check over 100% humidity - // Allow 0 because very low battery or defective sensor will report - // those values. - humidity = (bb[6] & 0x7f); - if (humidity > 100) { - decoder_logf(decoder, 1, __func__, "0x%04X Ch %s : Impossible humidity: %d %%rH", - sensor_id, channel_str, humidity); - return DECODE_FAIL_SANITY; - } - - if (humidity == 0) - exception++; - - - /* clang-format off */ - data = data_append(data, - "temperature_F", "temperature", DATA_FORMAT, "%.1f F", DATA_DOUBLE, tempf, - "humidity", NULL, DATA_FORMAT, "%u %%", DATA_INT, humidity, - NULL); - /* clang-format on */ - } - - if (message_type == ACURITE_MSGTYPE_ATLAS_WNDSPD_RAIN || - message_type == ACURITE_MSGTYPE_ATLAS_WNDSPD_RAIN_LTNG) { - // Wind speed, wind direction, and rain fall - - // Wind direction is in degrees, 0-360, only 9 bits needed - // but historically decoded as 10 bits. - // There seems to be 11 bits available - // As with temperatuve message, flag msg if those two extra bits - // are ever non-zero so they can be investigated - // Note: output as float, but currently can only be decoded an integer - wind_dir = ((bb[4] & 0x1f) << 5) | ((bb[5] & 0x7c) >> 2); - if ((bb[4] & 0x30) != 0) - exception++; - - if (wind_dir > 360) { - decoder_logf(decoder, 1, __func__, "Atlas 0x%04X Ch %s, invalid wind direction: %0.1fF", - sensor_id, channel_str, wind_dir); - return DECODE_FAIL_SANITY; - } - - // range: 0 to 5.11 in, 0.01 inch increments, accumulated - // JRH: Confirmed 9 bits, counter rolls over after 5.11 inches - raincounter = ((bb[5] & 0x03) << 7) | (bb[6] & 0x7F); - - /* clang-format off */ - data = data_append(data, - "wind_dir_deg", NULL, DATA_FORMAT, "%.1f", DATA_DOUBLE, wind_dir, - "rain_in", "Rainfall Accumulation", DATA_FORMAT, "%.2f in", DATA_DOUBLE, raincounter * 0.01f, - NULL); - /* clang-format on */ - } - - if (message_type == ACURITE_MSGTYPE_ATLAS_WNDSPD_UV_LUX || - message_type == ACURITE_MSGTYPE_ATLAS_WNDSPD_UV_LUX_LTNG) { - // Wind speed, UV Index, Light Intensity, and optionally Lightning - - // Spec UV index is 0-16 (but can only be 0-15) - int uv = (bb[4] & 0x0f); - - // Light intensity 0 - 120,000 lumens / 10 - // 14 bits are available (0-16,383) - int lux = ((bb[5] & 0x7f) << 7) | (bb[6] & 0x7F); - if (lux > 12000) { - decoder_logf(decoder, 1, __func__, "Atlas 0x%04X Ch %s, invalid lux %d", - sensor_id, channel_str, lux); - return DECODE_FAIL_SANITY; - } - - /* clang-format off */ - data = data_append(data, - "uv", NULL, DATA_INT, uv, - "lux", NULL, DATA_INT, lux * 10, - NULL); - /* clang-format on */ - } - - if ((message_type == ACURITE_MSGTYPE_ATLAS_WNDSPD_TEMP_HUM_LTNG || - message_type == ACURITE_MSGTYPE_ATLAS_WNDSPD_RAIN_LTNG || - message_type == ACURITE_MSGTYPE_ATLAS_WNDSPD_UV_LUX_LTNG)) { - - // @todo decode strike_distance to miles or KM. - int strike_count = ((bb[7] & 0x7f) << 2) | ((bb[8] & 0x60) >> 5); - int strike_distance = bb[8] & 0x1f; - - /* clang-format off */ - data = data_append(data, - "strike_count", NULL, DATA_INT, strike_count, - "strike_distance", NULL, DATA_INT, strike_distance, - NULL); - /* clang-format on */ - } - - // @todo only do this if exception != 0, but would be somewhat incompatible - data = data_append(data, - "exception", "data_exception", DATA_INT, exception, - "raw_msg", "raw_message", DATA_STRING, raw_str, - NULL); - - decoder_output_data(decoder, data); - - return 1; // one valid message decoded -} - -/** -Acurite 592TXR Temperature Humidity sensor decoder. - -Also: -- Acurite 592TX (without humidity sensor) - -Message Type 0x04, 7 bytes - -| Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | -| --------- | --------- | --------- | --------- | --------- | --------- | --------- | -| CCII IIII | IIII IIII | pB00 0100 | pHHH HHHH | p??T TTTT | pTTT TTTT | KKKK KKKK | - - -- C: Channel 00: C, 10: B, 11: A, (01 is invalid) -- I: Device ID (14 bits) -- B: Battery, 1 is battery OK, 0 is battery low -- M: Message type (6 bits), 0x04 -- T: Temperature Celsius (11 - 14 bits?), + 1000 * 10 -- H: Relative Humidity (%) (7 bits) -- K: Checksum (8 bits) -- p: Parity bit - -Notes: - -- Temperature - - Encoded as Celsius + 1000 * 10 - - only 11 bits needed for specified range -40 C to 70 C (-40 F - 158 F) - - However 14 bits available for temperature, giving possible range of -100 C to 1538.4 C - - @todo - check if high 3 bits ever used for anything else - -*/ -static int acurite_tower_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t *bb) -{ - // MIC (checkum, parity) validated in calling function - - (void)bitbuffer; - int exception = 0; - char const* channel_str = acurite_getChannel(bb[0]); - int sensor_id = ((bb[0] & 0x3f) << 8) | bb[1]; - int battery_low = (bb[2] & 0x40) == 0; - - // Spec is relative humidity 1-99% - // Allowing value of 0, very low battery or broken sensor can return 0% or 1% - int humidity = (bb[3] & 0x7f); - if (humidity > 100 && humidity != 127) { - decoder_logf(decoder, 1, __func__, "0x%04X Ch %s : invalid humidity: %d %%rH", - sensor_id, channel_str, humidity); - return DECODE_FAIL_SANITY; - } - - // temperature encoding used by "tower" sensors 592txr - // 14 bits available after removing both parity bits. - // 11 bits needed for specified range -40 C to 70 C (-40 F - 158 F) - // Possible ranges are -100 C to 1538.4 C, but most of that range - // is not possible on Earth. - // pIII IIII pIII IIII - int temp_raw = ((bb[4] & 0x7F) << 7) | (bb[5] & 0x7F); - float tempc = (temp_raw - 1000) * 0.1f; - if (tempc < -40 || tempc > 70) { - decoder_logf(decoder, 1, __func__, "0x%04X Ch %s : invalid temperature: %0.2f C", - sensor_id, channel_str, tempc); - return DECODE_FAIL_SANITY; - } - - // flag if bits 12-14 of temperature are ever non-zero - // so they can be investigated for other possible information - if ((temp_raw & 0x3800) != 0) - exception++; - - data_t* data; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-Tower", - "id", "", DATA_INT, sensor_id, - "channel", NULL, DATA_STRING, channel_str, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, tempc, - "humidity", "Humidity", DATA_COND, humidity != 127, DATA_FORMAT, "%u %%", DATA_INT, humidity, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ - - if (exception) - data_append_exception(data, exception, bb, ACURITE_TXR_BYTELEN); - - decoder_output_data(decoder, data); - - return 1; -} - -/** -Acurite 1190/1192 leak detector - -Note: it seems like Acurite has deleted this product and -related information from their website so specs, manual, etc. -aren't easy to find - -*/ -static int acurite_1190_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t *bb) -{ - (void)bitbuffer; - // Channel is the first two bits of the 0th byte - // but only 3 of the 4 possible values are valid - char const* channel_str = acurite_getChannel(bb[0]); - - // Tower sensor ID is the last 14 bits of byte 0 and 1 - // CCII IIII | IIII IIII - int sensor_id = ((bb[0] & 0x3f) << 8) | bb[1]; - - // Battery status is the 7th bit 0x40. 1 = normal, 0 = low - int battery_low = (bb[2] & 0x40) == 0; - - // Leak indicator bit is the 5th bit of byte 3. 1 = wet, 0 = dry - int is_wet = (bb[3] & 0x10) >> 4; - - data_t* data; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-Leak", - "id", "", DATA_INT, sensor_id, - "channel", NULL, DATA_STRING, channel_str, - "battery_ok", "Battery", DATA_INT, !battery_low, - "leak_detected", "Leak", DATA_INT, is_wet, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - - return 1; -} - -/** -Decode Acurite 515 Refrigerator/Freezer sensors - -Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 -CCII IIII | IIII IIII | pBMM MMMM | bTTT TTTT | bTTT TTTT | KKKK KKKK - -- C: Channel 00: C, 10: B, 11: A -- I: Device ID (14 bits), volatie, resets at power up -- B: Battery, 1 is battery OK, 0 is battery low -- M: Message type (6 bits), 0x8: Refrigerator, 0x9: Freezer -- T: Temperature Fahrenheit (14 bits?), + 1480 * 10 -- K: Checksum (8 bits) -- p: Parity bit - -*/ -static int acurite_515_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t *bb) -{ - // length, MIC (checkum, parity) validated in calling function - - (void)bitbuffer; - int exception = 0; - char channel_type_str[3]; - uint8_t message_type = bb[2] & 0x3f; - - // Channel A, B, C, common with other Acurite devices - char const* channel_str = acurite_getChannel(bb[0]); - - channel_type_str[0] = channel_str[0]; - - if (message_type == ACURITE_MSGTYPE_515_REFRIGERATOR) - channel_type_str[1] = 'R'; - else if (message_type == ACURITE_MSGTYPE_515_FREEZER) - channel_type_str[1] = 'F'; - else { - decoder_logf(decoder, 1, __func__, "unknown message type 0x02%x", message_type); - return DECODE_FAIL_SANITY; - } - - channel_type_str[2] = 0; - - // Sensor ID is the last 14 bits of byte 0 and 1 - // CCII IIII | IIII IIII - // The sensor ID changes on each power-up of the sensor. - uint16_t sensor_id = ((bb[0] & 0x3f) << 8) | bb[1]; - - // temperature encoding 14 bits after removing both parity bits. - // Spec range from Manual: -40 F to 158 F (-40 to 70 C) - // Offset to avoid negative values is 1480 - // Possible encoding range with 14 bits (0-16383) is -148.0 F to 1490.3 F - // Only 12 bits needed to represent -40 F to 158 F with encoding offset of 1480. - // encoding range at 12 bits with +1480 offset: -148.0 F to +261.5 F - int temp_raw = ((bb[3] & 0x7F) << 7) | (bb[4] & 0x7F); - float tempf = (temp_raw - 1480) * 0.1f; - if (tempf < -40.0 || tempf > 158.0) { - decoder_logf(decoder, 1, __func__, "515 0x%04X Ch %s, invalid temperature: %0.1f F", - sensor_id, channel_str, tempf); - return DECODE_FAIL_SANITY; - } - - // flag if bits 13 - 14 of temperature are ever non-zero - // so they can be investigated - if ((temp_raw & 0x3000) != 0) - exception++; - - // Battery status is the 7th bit 0x40. 1 = normal, 0 = low - int battery_low = (bb[2] & 0x40) == 0; - - data_t* data; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-515", - "id", "", DATA_INT, sensor_id, - "channel", NULL, DATA_STRING, channel_type_str, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_F", "Temperature", DATA_FORMAT, "%.1f F", DATA_DOUBLE, tempf, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ - - if (exception) - data_append_exception(data, exception, bb, ACURITE_515_BYTELEN); - - decoder_output_data(decoder, data); - - return 1; -} - -/** -Check Acurite TXR message integrity (length, checksum, parity) - -Need to pass in expected length - correct number of bytes for -that message type. - -Return 0 for valid roe or DECODE_ABORT_LENGHT, DECODE_FAIL_MIC, DECODE_FAIL_SANITY - -Long rows with extra bits/bytes (from demod/bit slicing) -will be accepted as long the bytes up to the expected length -pass checksum and parity tests. -*/ -static int acurite_txr_check(r_device *decoder, uint8_t const bb[], unsigned browlen, unsigned explen) -{ - - // Currently shortest Acurite "TXR" message is 6 bytes - // 5 bytes could possibly be valid, but would only have - // a single data byte after Channel, ID, message type, and checksum - // Really short rows (1-2) bytes, should be rejected quietly earlier - // so real error types can be seen - if (browlen < 6) - return DECODE_ABORT_LENGTH; - - if (browlen < explen) { - decoder_log_bitrow(decoder, 1, __func__, bb, browlen * 8, "wrong length for msg type"); - return DECODE_ABORT_LENGTH; - } - - // 8 bit checksum in the last byte - if ((add_bytes(bb, explen - 1) & 0xff) != bb[explen - 1]) { - decoder_log_bitrow(decoder, 1, __func__, bb, browlen * 8, "bad checksum"); - return DECODE_FAIL_MIC; - } - - // Verify parity bits - // Bytes 2 ... n-1 should all have even parity - // (ID bytes and checksum byte are all 8 bit, so no parity check) - int parity = parity_bytes(&bb[2], explen - 3); - - if (parity) { - decoder_log_bitrow(decoder, 1, __func__, bb, browlen * 8,"bad parity"); - return DECODE_FAIL_MIC; - } - - // All of these devices have channel (A, B, C) in two bits (mask 0c0) of byte 0 - // 00: C, 10: B, 11: A, (01 aka 'E' is invalid) - // check sanity to cut down an bad messages that pass MIC checks - char const *channel_str = acurite_getChannel(bb[0]); - if (*channel_str == 'E') { - uint8_t message_type = bb[2] & 0x3f; - decoder_logf(decoder, 1, __func__, - "bad channel Ch %s, msg type 0x%02x, msg len %d", - channel_str, message_type, browlen); - return DECODE_FAIL_SANITY; - } - - return 0; -} - -/** -Process messages for Acurite weather stations, tower and related sensors -@sa acurite_1190_decode() -@sa acurite_515_decode() -@sa acurite_6045_decode() -@sa acurite_899_decode() -#sa acurite_3n1_decode() -@sa acurite_5n1_decode() -@sa acurite_atlas_decode() -@sa acurite_tower_decode() - -This callback is used for devices that use a very similar message format: - -- 592TXR / 592TX / 6002RM / 6044m Tower sensor and related temperature/humidity sensors -- Atlas (7-in-1) Weather Station -- Iris (5-in-1) weather station -- Notos (3-in-1) Weather station -- 6045M Lightning Detector with Temperature and Humidity -- 899 Rain Fall Gauge -- 515 Refrigerator/Freezer sensors -- 1190/1192 Water alarm - -These devices have a message type in the 3rd byte and an 8 bit checksum -in the last byte. - -*/ -static int acurite_txr_callback(r_device *decoder, bitbuffer_t *bitbuffer) -{ - int decoded = 0; - int error_ret = 0; - int ret = 0; - uint8_t *bb; - uint8_t message_type; - - bitbuffer_invert(bitbuffer); - - for (uint16_t brow = 0; brow < bitbuffer->num_rows; ++brow) { - int row_bit_cnt = bitbuffer->bits_per_row[brow]; - int browlen = row_bit_cnt / 8; // assumption: safe to round down, extra bits are spurious - - bb = bitbuffer->bb[brow]; - - // Known messages in this family are between 6 and 10 bytes - if (browlen < 6) { - continue; // quietly skip short rows - } - - // Currently known longest message is 10 bytes (Atlas with lightning sensorr) - if (browlen > 10) { - decoder_logf(decoder, 2, __func__, "Skipping wrong len row %u bits %u, bytes %d", - brow, row_bit_cnt, browlen); - error_ret = DECODE_ABORT_LENGTH; - continue; - } - - decoder_logf(decoder, 2, __func__, - "row %u bits %u, bytes %d, extra bits %d, msg type 0x%02x", - brow, row_bit_cnt, browlen, row_bit_cnt % 8, bb[2] & 0x3f); - - - // quietly ignore rows of zeros (ID, msg type, checksum) - if (bb[0] == 0 && bb[1] == 0 && bb[2] == 0 && bb[browlen - 1] == 0) - continue; - - // acurite sensors with a common format have a message type - // in the lower 6 bits of the 3rd byte. - // Format: PBMMMMMM - // P = Parity - // B = Battery Normal - // M = Message type - message_type = bb[2] & 0x3f; - - // Check so that unknown message type can be flagged - // and dispatching to decoders can be easier to maintain - switch(message_type) { - case ACURITE_MSGTYPE_1190_DETECTOR: - case ACURITE_MSGTYPE_TOWER_SENSOR: - case ACURITE_MSGTYPE_6045M: - case ACURITE_MSGTYPE_5N1_WINDSPEED_WINDDIR_RAINFALL: - case ACURITE_MSGTYPE_5N1_WINDSPEED_TEMP_HUMIDITY: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_TEMP_HUM: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_RAIN: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_UV_LUX: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_TEMP_HUM_LTNG: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_RAIN_LTNG: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_UV_LUX_LTNG: - case ACURITE_MSGTYPE_515_REFRIGERATOR: - case ACURITE_MSGTYPE_515_FREEZER: - case ACURITE_MSGTYPE_3N1_WINDSPEED_TEMP_HUMIDITY: - case ACURITE_MSGTYPE_899_RAINFALL: - break; - - default: - decoder_log_bitrow(decoder, 1, __func__, bb, row_bit_cnt, - "Unknown message type"); - error_ret = DECODE_FAIL_SANITY; - continue; - break; - } - - // Check message type and dispatch to appropriate decoders - // NOTE: since we are processing each row, do not return - // until all rows have been processed - if (message_type == ACURITE_MSGTYPE_TOWER_SENSOR) { - if ((ret = acurite_txr_check(decoder, bb, browlen, ACURITE_TXR_BYTELEN)) != 0) { - error_ret = ret; - } else { - if ((ret = acurite_tower_decode(decoder, bitbuffer, bb)) > 0) { - decoded += ret; - } else if (ret < 0) { - error_ret = ret; - } - } - } - - if (message_type == ACURITE_MSGTYPE_1190_DETECTOR) { - if ((ret = acurite_txr_check(decoder, bb, browlen, ACURITE_1190_BYTELEN)) != 0) { - error_ret = ret; - } else { - if ((ret = acurite_1190_decode(decoder, bitbuffer, bb)) > 0) { - decoded += ret; - } else if (ret < 0) { - error_ret = ret; - } - } - } - - if (message_type == ACURITE_MSGTYPE_6045M) { - if ((ret = acurite_txr_check(decoder, bb, browlen, ACURITE_6045_BYTELEN)) != 0) { - error_ret = ret; - } else { - if ((ret = acurite_6045_decode(decoder, bitbuffer, brow)) > 0) { - decoded += ret; - } else if (ret < 0) { - error_ret = ret; - } - } - } - - if (message_type == ACURITE_MSGTYPE_515_REFRIGERATOR || - message_type == ACURITE_MSGTYPE_515_FREEZER) { - if ((ret = acurite_txr_check(decoder, bb, browlen, ACURITE_515_BYTELEN)) != 0) { - error_ret = ret; - } else { - if ((ret = acurite_515_decode(decoder, bitbuffer, bb)) > 0) { - decoded += ret; - } else if (ret < 0) { - error_ret = ret; - } - } - } - - if (message_type == ACURITE_MSGTYPE_5N1_WINDSPEED_TEMP_HUMIDITY || - message_type == ACURITE_MSGTYPE_5N1_WINDSPEED_WINDDIR_RAINFALL) { - if ((ret = acurite_txr_check(decoder, bb, browlen, ACURITE_5N1_BYTELEN)) != 0) { - error_ret = ret; - } else { - if ((ret = acurite_5n1_decode(decoder, bitbuffer, bb)) > 0) { - decoded += ret; - } else if (ret < 0) { - error_ret = ret; - } - } - } - - if (message_type == ACURITE_MSGTYPE_3N1_WINDSPEED_TEMP_HUMIDITY) { - /* - @todo - does 3n1 use parity checking? - 3n1 g001 in rtl_433_test has odd parity the 2nd to last byte in both copies - but g002 passes parity check - */ - - if (browlen < ACURITE_3N1_BYTELEN) { - decoder_log_bitrow(decoder, 1, __func__, bb, browlen * 8, "3n1 wrong length"); - error_ret = DECODE_ABORT_LENGTH; - continue; - } - - if ((add_bytes(bb, ACURITE_3N1_BYTELEN - 1) & 0xff) != - bb[ACURITE_3N1_BYTELEN - 1]) { - decoder_log_bitrow(decoder, 1, __func__, bb, browlen * 8, "bad checksum"); - error_ret = DECODE_FAIL_MIC; - continue; - } - - if ((ret = acurite_3n1_decode(decoder, bitbuffer, bb)) > 0) { - decoded += ret; - } else if (ret < 0) { - error_ret = ret; - } - } - - if (message_type == ACURITE_MSGTYPE_899_RAINFALL) { - /* - @todo - does the 899 use parity checking? - The available sample shows a parity bit in the message byte - but there isn't enough accumulated rain in the data bytes - to see if parity is used - */ - if ((ret = acurite_txr_check(decoder, bb, browlen, ACURITE_899_BYTELEN)) != 0) { - error_ret = ret; - } else { - if ((ret = acurite_899_decode(decoder, bitbuffer, bb)) > 0) { - decoded += ret; - } else if (ret < 0) { - error_ret = ret; - } - } - } - - // process Atlas - switch(message_type) { - // Atlas messages without lightning sensor installed - 8 bytes - case ACURITE_MSGTYPE_ATLAS_WNDSPD_TEMP_HUM: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_RAIN: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_UV_LUX: - if ((ret = acurite_txr_check(decoder, bb, browlen, ACURITE_ATLAS_BYTELEN)) != 0) { - error_ret = ret; - } else { - if ((ret = acurite_atlas_decode(decoder, bitbuffer, brow)) > 0) { - decoded += ret; - } else if (ret < 0) { - error_ret = ret; - } - } - break; - - // Atlas messages with lightning sensor installed - 10 bytes - case ACURITE_MSGTYPE_ATLAS_WNDSPD_TEMP_HUM_LTNG: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_RAIN_LTNG: - case ACURITE_MSGTYPE_ATLAS_WNDSPD_UV_LUX_LTNG: - if ((ret = acurite_txr_check(decoder, bb, browlen, ACURITE_ATLAS_LTNG_BYTELEN)) != 0) { - error_ret = ret; - } else { - if ((ret = acurite_atlas_decode(decoder, bitbuffer, brow)) > 0) { - decoded += ret; - } else if (ret < 0) { - error_ret = ret; - } - } - break; - - } - - decoder_logf(decoder, 2, __func__, - "stats: row %u, msg type 0x%02x, bytes %d, decoded %d, error %d", - brow, message_type, browlen, decoded, error_ret); - - } - - if (decoded > 0) - return decoded; - else - return error_ret; -} - - -/** -Acurite 00986 Refrigerator / Freezer Thermometer. - -Includes two sensors and a display, labeled 1 and 2, -by default 1 - Refrigerator, 2 - Freezer. - -PPM, 5 bytes, sent twice, no gap between repeaters -start/sync pulses two short, with short gaps, followed by -4 long pulse/gaps. - -@todo, the 2 short sync pulses get confused as data. - -Data Format - 5 bytes, sent LSB first, reversed: - - TT II II SS CC -- T - Temperature in Fahrenheit, integer, MSB = sign. - Encoding is "Sign and magnitude" -- I - 16 bit sensor ID - changes at each power up -- S - status/sensor type - 0x01 = Sensor 2 - 0x02 = low battery -- C = CRC (CRC-8 poly 0x07, little-endian) - -@todo -- needs new PPM demod that can separate out the short - start/sync pulses which confuse things and cause - one data bit to be lost in the check value. - -2018-04 A user with a dedicated receiver indicated the - possibility that the transmitter actually drops the - last bit instead of the demod. - -leaving some of the debugging code until the missing -bit issue gets resolved. -*/ -static int acurite_986_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - int const browlen = 5; - uint8_t *bb, sensor_num, status, crc, crcc; - uint8_t br[8]; - int8_t tempf; // Raw Temp is 8 bit signed Fahrenheit - uint16_t sensor_id, valid_cnt = 0; - char sensor_type; - char const *channel_str; - int battery_low; - data_t *data; - - int result = 0; - - for (uint16_t brow = 0; brow < bitbuffer->num_rows; ++brow) { - - decoder_logf(decoder, 2, __func__, "row %u bits %u, bytes %d", brow, bitbuffer->bits_per_row[brow], browlen); - - if (bitbuffer->bits_per_row[brow] < 39 || bitbuffer->bits_per_row[brow] > 43) { - if (bitbuffer->bits_per_row[brow] > 16) - decoder_log(decoder, 2, __func__,"skipping wrong len"); - result = DECODE_ABORT_LENGTH; - continue; // DECODE_ABORT_LENGTH - } - bb = bitbuffer->bb[brow]; - - // Reduce false positives - // may eliminate these with a better PPM (precise?) demod. - if ((bb[0] == 0xff && bb[1] == 0xff && bb[2] == 0xff) || - (bb[0] == 0x00 && bb[1] == 0x00 && bb[2] == 0x00)) { - result = DECODE_ABORT_EARLY; - continue; // DECODE_ABORT_EARLY - } - - // Reverse the bits, msg sent LSB first - for (int i = 0; i < browlen; i++) - br[i] = reverse8(bb[i]); - - decoder_log_bitrow(decoder, 1, __func__, br, browlen * 8, "reversed"); - - tempf = br[0]; - sensor_id = (br[1] << 8) + br[2]; - status = br[3]; - sensor_num = (status & 0x01) + 1; - status = status >> 1; - battery_low = ((status & 1) == 1); - - // By default Sensor 1 is the Freezer, 2 Refrigerator - sensor_type = sensor_num == 2 ? 'F' : 'R'; - channel_str = sensor_num == 2 ? "2F" : "1R"; - - crc = br[4]; - crcc = crc8le(br, 4, 0x07, 0); - - if (crcc != crc) { - decoder_logf_bitrow(decoder, 2, __func__, br, browlen * 8, "bad CRC: %02x -", crc8le(br, 4, 0x07, 0)); - // HACK: rct 2018-04-22 - // the message is often missing the last 1 bit either due to a - // problem with the device or demodulator - // Add 1 (0x80 because message is LSB) and retry CRC. - if (crcc == (crc | 0x80)) { - decoder_logf(decoder, 2, __func__, "CRC fix %02x - %02x", crc, crcc); - } - else { - continue; // DECODE_FAIL_MIC - } - } - - if (tempf & 0x80) { - tempf = (tempf & 0x7f) * -1; - } - - decoder_logf(decoder, 1, __func__, "sensor 0x%04x - %d%c: %d F", sensor_id, sensor_num, sensor_type, tempf); - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-986", - "id", NULL, DATA_INT, sensor_id, - "channel", NULL, DATA_STRING, channel_str, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_F", "temperature", DATA_FORMAT, "%f F", DATA_DOUBLE, (float)tempf, - "status", "status", DATA_INT, status, - "mic", "Integrity", DATA_STRING, "CRC", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - - valid_cnt++; - } - - if (valid_cnt) - return 1; - - return result; -} - -/** -Acurite 606 Temperature sensor - -*/ -static int acurite_606_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - data_t *data; - uint8_t *b; - int row; - int16_t temp_raw; // temperature as read from the data packet - float temp_c; // temperature in C - int battery_ok; // the battery status: 1 is good, 0 is low - int sensor_id; // the sensor ID - basically a random number that gets reset whenever the battery is removed - - row = bitbuffer_find_repeated_row(bitbuffer, 3, 32); // expected are 6 rows - if (row < 0) - return DECODE_ABORT_EARLY; - - if (bitbuffer->bits_per_row[row] > 33) - return DECODE_ABORT_LENGTH; - - b = bitbuffer->bb[row]; - - if (b[4] != 0) - return DECODE_FAIL_SANITY; - - // reject all blank messages - if (b[0] == 0 && b[1] == 0 && b[2] == 0 && b[3] == 0) - return DECODE_FAIL_SANITY; - - // calculate the checksum and only continue if we have a matching checksum - uint8_t chk = lfsr_digest8(b, 3, 0x98, 0xf1); - if (chk != b[3]) - return DECODE_FAIL_MIC; - - // Processing the temperature: - // Upper 4 bits are stored in nibble 1, lower 8 bits are stored in nibble 2 - // upper 4 bits of nibble 1 are reserved for other usages (e.g. battery status) - sensor_id = b[0]; - battery_ok = (b[1] & 0x80) >> 7; - temp_raw = (int16_t)((b[1] << 12) | (b[2] << 4)); - temp_raw = temp_raw >> 4; - temp_c = temp_raw * 0.1f; - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-606TX", - "id", "", DATA_INT, sensor_id, - "battery_ok", "Battery", DATA_INT, battery_ok, - "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - return 1; -} - -/** -Acurite 590TX temperature/humidity sensor - -*/ -static int acurite_590tx_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - data_t *data; - uint8_t *b; - int row; - int sensor_id; // the sensor ID - basically a random number that gets reset whenever the battery is removed - int battery_ok; // the battery status: 1 is good, 0 is low - int channel; - int humidity; - int temp_raw; // temperature as read from the data packet - float temp_c; // temperature in C - - row = bitbuffer_find_repeated_row(bitbuffer, 3, 25); // expected are min 3 rows - if (row < 0) - return DECODE_ABORT_EARLY; - - if (bitbuffer->bits_per_row[row] > 25) - return DECODE_ABORT_LENGTH; - - b = bitbuffer->bb[row]; - - if (b[4] != 0) // last byte should be zero - return DECODE_FAIL_SANITY; - - // reject rows that are mostly zero - if (b[0] == 0 && b[1] == 0 && b[2] == 0 && b[3] == 0) - return DECODE_FAIL_SANITY; - - // parity check: odd parity on bits [0 .. 10] - // i.e. 8 bytes and another 2 bits. - uint8_t parity = b[0]; // parity as byte - parity = (parity >> 4) ^ (parity & 0xF); // fold to nibble - parity = (parity >> 2) ^ (parity & 0x3); // fold to 2 bits - parity ^= b[1] >> 6; // add remaining bits - parity = (parity >> 1) ^ (parity & 0x1); // fold to 1 bit - - if (!parity) { - decoder_log(decoder, 1, __func__, "parity check failed"); - return DECODE_FAIL_MIC; - } - - // Processing the temperature: - // Upper 4 bits are stored in nibble 1, lower 8 bits are stored in nibble 2 - // upper 4 bits of nibble 1 are reserved for other usages (e.g. battery status) - sensor_id = b[0] & 0xFE; //first 6 bits and it changes each time it resets or change the battery - battery_ok = (b[0] & 0x01); //1=ok, 0=low battery - //next 2 bits are checksum - //next two bits are identify ID (maybe channel ?) - channel = (b[1] >> 4) & 0x03; - - temp_raw = (int16_t)(((b[1] & 0x0F) << 12) | (b[2] << 4)); - temp_raw = temp_raw >> 4; - temp_c = (temp_raw - 500) * 0.1f; // NOTE: there seems to be a 50 degree offset? - - if (temp_raw >= 0 && temp_raw <= 100) // NOTE: no other way to differentiate humidity from temperature? - humidity = temp_raw; - else - humidity = -1; - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Acurite-590TX", - "id", "", DATA_INT, sensor_id, - "battery_ok", "Battery", DATA_INT, battery_ok, - "channel", "Channel", DATA_INT, channel, - "humidity", "Humidity", DATA_COND, humidity != -1, DATA_INT, humidity, - "temperature_C", "Temperature", DATA_COND, humidity == -1, DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, - "mic", "Integrity", DATA_STRING, "PARITY", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - return 1; -} - -/** -Acurite 00275rm Room Monitor sensors - -*/ -static int acurite_00275rm_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - int result = 0; - bitbuffer_invert(bitbuffer); - - // This sensor repeats a signal three times. Combine as fallback. - uint8_t *b_rows[3] = {0}; - int n_rows = 0; - for (int row = 0; row < bitbuffer->num_rows; ++row) { - if (n_rows < 3 && bitbuffer->bits_per_row[row] == 88) { - b_rows[n_rows] = bitbuffer->bb[row]; - n_rows++; - } - } - - // Combine signal if exactly three repeats were found - if (n_rows == 3) { - bitbuffer_add_row(bitbuffer); - uint8_t *b = bitbuffer->bb[bitbuffer->num_rows - 1]; - for (int i = 0; i < 11; ++i) { - // The majority bit count wins - b[i] = (b_rows[0][i] & b_rows[1][i]) | - (b_rows[1][i] & b_rows[2][i]) | - (b_rows[2][i] & b_rows[0][i]); - } - bitbuffer->bits_per_row[bitbuffer->num_rows - 1] = 88; - } - - // Output the first valid row - for (int row = 0; row < bitbuffer->num_rows; ++row) { - if (bitbuffer->bits_per_row[row] != 88) { - result = DECODE_ABORT_LENGTH; - continue; // return DECODE_ABORT_LENGTH; - } - uint8_t *b = bitbuffer->bb[row]; - - // Check CRC - if (crc16lsb(b, 11, 0x00b2, 0x00d0) != 0) { - decoder_log_bitrow(decoder, 1, __func__, b, 11 * 8, "sensor bad CRC"); - result = DECODE_FAIL_MIC; - continue; // return DECODE_FAIL_MIC; - } - - // Decode common fields - int id = (b[0] << 16) | (b[1] << 8) | b[3]; - int battery_low = (b[2] & 0x40) == 0; - int model_flag = (b[2] & 1); - int temp_raw = (b[4] << 4) | (b[5] >> 4); - float tempc = (temp_raw - 1000) * 0.1f; - int probe = b[5] & 3; - int humidity = ((b[6] & 0x1f) << 2) | (b[7] >> 6); - - // Water probe (detects water leak) - int water = (b[7] & 0x0f) == 15; // valid only if (probe == 1) - // Soil probe (detects temperature) - int ptemp_raw = ((b[7] & 0x0f) << 8) | (b[8]); // valid only if (probe == 2 || probe == 3) - float ptempc = (ptemp_raw - 1000) * 0.1f; - // Spot probe (detects temperature and humidity) - int phumidity = b[9] & 0x7f; // valid only if (probe == 3) - - /* clang-format off */ - data_t *data = data_make( - "model", "", DATA_STRING, model_flag ? "Acurite-00275rm" : "Acurite-00276rm", - "subtype", "Probe", DATA_INT, probe, - "id", "", DATA_INT, id, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_C", "Celsius", DATA_FORMAT, "%.1f C", DATA_DOUBLE, tempc, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - "water", "", DATA_COND, probe == 1, DATA_INT, water, - "temperature_1_C", "Celsius", DATA_COND, probe == 2, DATA_FORMAT, "%.1f C", DATA_DOUBLE, ptempc, - "temperature_1_C", "Celsius", DATA_COND, probe == 3, DATA_FORMAT, "%.1f C", DATA_DOUBLE, ptempc, - "humidity_1", "Humidity", DATA_COND, probe == 3, DATA_FORMAT, "%u %%", DATA_INT, phumidity, - "mic", "Integrity", DATA_STRING, "CRC", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - - return 1; - } - // Only returns the latest result, but better than nothing. - return result; -} - -static char const *const acurite_rain_gauge_output_fields[] = { - "model", - "id", - "rain_mm", - NULL, -}; - -r_device const acurite_rain_896 = { - .name = "Acurite 896 Rain Gauge", - .modulation = OOK_PULSE_PPM, - .short_width = 1000, - .long_width = 2000, - .gap_limit = 3500, - .reset_limit = 5000, - .decode_fn = &acurite_rain_896_decode, - .priority = 10, // Eliminate false positives by letting oregon scientific v1 protocol go earlier - .fields = acurite_rain_gauge_output_fields, -}; - -static char const *const acurite_th_output_fields[] = { - "model", - "id", - "battery_ok", - "temperature_C", - "humidity", - "status", - "mic", - NULL, -}; - -r_device const acurite_th = { - .name = "Acurite 609TXC Temperature and Humidity Sensor", - .modulation = OOK_PULSE_PPM, - .short_width = 1000, - .long_width = 2000, - .gap_limit = 3000, - .reset_limit = 10000, - .decode_fn = &acurite_th_decode, - .fields = acurite_th_output_fields, -}; - -/* - * For Acurite 592 TXR Temp/Humidity, but - * Should match Acurite 592TX, 5-n-1, etc. - */ -static char const *const acurite_txr_output_fields[] = { - "model", - "message_type", // TODO: remove this - "id", - "channel", - "sequence_num", - "battery_ok", - "leak_detected", - "temperature_C", - "temperature_F", - "humidity", - "wind_avg_mi_h", - "wind_avg_km_h", - "wind_dir_deg", - "rain_in", - "rain_mm", - "storm_dist", - "strike_count", - "strike_distance", - "uv", - "lux", - "active", - "exception", - "raw_msg", - "rfi", - "mic", - NULL, -}; - -r_device const acurite_txr = { - .name = "Acurite 592TXR Temp/Humidity, 592TX Temp, 5n1 Weather Station, 6045 Lightning, 899 Rain, 3N1, Atlas", - .modulation = OOK_PULSE_PWM, - .short_width = 220, // short pulse is 220 us + 392 us gap - .long_width = 408, // long pulse is 408 us + 204 us gap - .sync_width = 620, // sync pulse is 620 us + 596 us gap - .gap_limit = 500, // longest data gap is 392 us, sync gap is 596 us - .reset_limit = 4000, // packet gap is 2192 us - .decode_fn = &acurite_txr_callback, - .fields = acurite_txr_output_fields, -}; - -/* - * Acurite 00986 Refrigerator / Freezer Thermometer - * - * Temperature only, Pulse Position - * - * A preamble: 2x of 216 us pulse + 276 us gap, 4x of 1600 us pulse + 1560 us gap - * 39 bits of data: 220 us pulses with short gap of 520 us or long gap of 880 us - * A transmission consists of two packets that run into each other. - * There should be 40 bits of data though. But the last bit can't be detected. - */ -static char const *const acurite_986_output_fields[] = { - "model", - "id", - "channel", - "battery_ok", - "temperature_F", - "status", - "mic", - NULL, -}; - -r_device const acurite_986 = { - .name = "Acurite 986 Refrigerator / Freezer Thermometer", - .modulation = OOK_PULSE_PPM, - .short_width = 520, - .long_width = 880, - .gap_limit = 1280, - .reset_limit = 4000, - .decode_fn = &acurite_986_decode, - .fields = acurite_986_output_fields, -}; - -/* - * Acurite 00606TX Tower Sensor - * - * Temperature only - * - */ - -static char const *const acurite_606_output_fields[] = { - "model", - "id", - "battery_ok", - "temperature_C", - "mic", - NULL, -}; - -static char const *const acurite_590_output_fields[] = { - "model", - "id", - "battery_ok", - "channel", - "temperature_C", - "humidity", - "mic", - NULL, -}; - -// actually tests/acurite/02/gfile002.cu8, check this -//.modulation = OOK_PULSE_PWM, -//.short_width = 576, -//.long_width = 1076, -//.gap_limit = 1200, -//.reset_limit = 12000, -r_device const acurite_606 = { - .name = "Acurite 606TX Temperature Sensor", - .modulation = OOK_PULSE_PPM, - .short_width = 2000, - .long_width = 4000, - .gap_limit = 7000, - .reset_limit = 10000, - .decode_fn = &acurite_606_decode, - .fields = acurite_606_output_fields, -}; - -static char const *const acurite_00275rm_output_fields[] = { - "model", - "subtype", - "id", - "battery_ok", - "temperature_C", - "humidity", - "water", - "temperature_1_C", - "humidity_1", - "mic", - NULL, -}; - -r_device const acurite_00275rm = { - .name = "Acurite 00275rm,00276rm Temp/Humidity with optional probe", - .modulation = OOK_PULSE_PWM, - .short_width = 232, // short pulse is 232 us - .long_width = 420, // long pulse is 420 us - .gap_limit = 520, // long gap is 384 us, sync gap is 592 us - .reset_limit = 708, // no packet gap, sync gap is 592 us - .sync_width = 632, // sync pulse is 632 us - .decode_fn = &acurite_00275rm_decode, - .fields = acurite_00275rm_output_fields, -}; - -r_device const acurite_590tx = { - .name = "Acurite 590TX Temperature with optional Humidity", - .modulation = OOK_PULSE_PPM, // OOK_PULSE_PWM, - .short_width = 500, // short pulse is 232 us - .long_width = 1500, // long pulse is 420 us - .gap_limit = 1484, // long gap is 384 us, sync gap is 592 us - .reset_limit = 3000, // no packet gap, sync gap is 592 us - .sync_width = 500, // sync pulse is 632 us - .decode_fn = &acurite_590tx_decode, - .fields = acurite_590_output_fields, -}; \ No newline at end of file diff --git a/contrib/govee.c b/contrib/govee.c deleted file mode 100644 index e8b1d817..00000000 --- a/contrib/govee.c +++ /dev/null @@ -1,407 +0,0 @@ -/** @file - Govee Water Leak Detector H5054, Door Contact Sensor B5023. - - 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. -*/ -/** -Govee Water Leak Detector H5054, Door Contact Sensor B5023. - -See https://www.govee.com/ - -Govee Water Leak Detector H5054: -https://www.govee.com/products/110/water-leak-detector - -Govee Door Contact Sensor B5023: -https://www.govee.com/products/27/govee-door-contact-sensor -https://www.govee.com/products/154/door-open-chimes-2-pack - -NOTE: The Govee Door Contact sensors only send a message when the contact - is opened. - Unfortunately, it does NOT send a message when the contact is closed. - -Data layout: - - II II ?E DD ?? XX - -- A data packet is 6 bytes, 48 bits. -- Bits are likely inverted (short=0, long=1) -- First 2 bytes are the ID. -- The upper nibble of byte 3 is unknown. - This upper nibble of the Water Leak Sensor is always 0. - This upper nibble of the Contact Sensor changes on different - Contact sensors, so perhaps it is a continuation of the ID? -- The lower nibble of byte 3 is the ACTION/EVENT. -- Byte 4 is the ACTION/EVENT data; battery percentage gauge for event 0xC. -- Byte 5 is unknown. -- Last byte contains the parity bits in index 2-6 (101PPPP1). - The parity checksum using CRC8 against the first 5 bytes - -Data decoding: - - ID:8h8h ?4h EVENT:4h EVENTDATA:8h ?8h CHK:3b 4h 1b - -Battery levels: - -- 100 : 5 Bars -- 095 : 4 Bars -- 059 : 4 Bars -- 026 : 3 Bars -- 024 : 2 Bars -- 001 : 1 Bars - -Raw data used to select checksum algorithm (after inverting to match used data): - - Binary Data: 01101111 00111010 11111010 11111010 11111000 10101111 - Parity value from last byte: 0111 - - Binary Data: 01101110 00011001 11111010 11111010 11111000 10101111 - Parity value from last byte: 0111 - - Binary Data: 01011100 01100110 11111010 11111010 11111000 10111101 - Parity value from last byte: 1110 - - Binary Data: 01101101 00011110 11111010 11111010 11111000 10100111 - Parity value from last byte: 0011 - - Binary Data: 01100111 11111001 11111010 11111010 11111000 10100001 - Parity value from last byte: 0000 - - Binary Data: 01101110 00101101 11111010 11111010 11111000 10100001 - Parity value from last byte: 0000 - - Binary Data: 01011100 00000111 11111010 11111010 11111000 10110011 - Parity value from last byte: 1001 - - Binary Data: 01101110 01000010 11111010 11111010 11111000 10110011 - Parity value from last byte: 1001 - - Binary Data: 01101110 00111010 11111010 11111010 11111000 10101101 - Parity value from last byte: 0110 - - Binary Data: 00100011 00000011 11111100 01001101 11111100 10110111 - Parity value from last byte: 1011 - - Binary Data: 00100011 00000011 11111100 01000111 11111100 10100011 - Parity value from last byte: 0001 - - Binary Data: 00100011 00000011 11111010 11111010 11111000 10101011 - Parity value from last byte: 0101 - - Binary Data: 00011001 01010111 11111100 01001110 11111100 10100001 - Parity value from last byte: 0000 - - Binary Data: 00110001 00010010 11111100 01000110 11111100 10100111 - Parity value from last byte: 0011 - - Binary Data: 00110001 00010010 11111101 11111101 11111100 10100101 - Parity value from last byte: 0010 - - Binary Data: 00110001 00010010 11111010 11111010 11111000 10101101 - Parity value from last byte: 0110 - - Binary Data: 01010110 00010100 11111010 11111010 11111000 10100011 - Parity value from last byte: 0001 - -RevSum input for parity (first 5 bytes, and the parity extracted from the last byte): - - 0x6f, 0x3a, 0xfa, 0xfa, 0xf8, 0x07 - 0x6e, 0x19, 0xfa, 0xfa, 0xf8, 0x07 - 0x5c, 0x66, 0xfa, 0xfa, 0xf8, 0x0e - 0x6d, 0x1e, 0xfa, 0xfa, 0xf8, 0x03 - 0x67, 0xf9, 0xfa, 0xfa, 0xf8, 0x00 - 0x6e, 0x2d, 0xfa, 0xfa, 0xf8, 0x00 - 0x5c, 0x07, 0xfa, 0xfa, 0xf8, 0x09 - 0x6e, 0x42, 0xfa, 0xfa, 0xf8, 0x09 - 0x6e, 0x3a, 0xfa, 0xfa, 0xf8, 0x06 - 0x23, 0x03, 0xfc, 0x4d, 0xfc, 0x0b - 0x23, 0x03, 0xfc, 0x47, 0xfc, 0x01 - 0x23, 0x03, 0xfa, 0xfa, 0xf8, 0x05 - 0x19, 0x57, 0xfc, 0x4e, 0xfc, 0x00 - 0x31, 0x12, 0xfc, 0x46, 0xfc, 0x03 - 0x31, 0x12, 0xfd, 0xfd, 0xfc, 0x02 - 0x31, 0x12, 0xfa, 0xfa, 0xf8, 0x06 - 0x56, 0x14, 0xfa, 0xfa, 0xf8, 0x01 - -*/ - -#include "decoder.h" - -#define GOVEE_WATER 5054 -#define GOVEE_CONTACT 5023 - -#define GOVEE_H5054_BYTELEN 6 -#define GOVEE_H5054_BITLEN 48 - -static int govee_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - int model_num = GOVEE_WATER; - - if (bitbuffer->num_rows < 3) { - return DECODE_ABORT_EARLY; // truncated transmission - } - - int r = bitbuffer_find_repeated_row(bitbuffer, 3, 6 * 8); - if (r < 0) { - return DECODE_ABORT_EARLY; - } - - if (bitbuffer->bits_per_row[r] > 6 * 8) { - return DECODE_ABORT_LENGTH; - } - - uint8_t *b = bitbuffer->bb[r]; - - // dump raw input code - char code_str[13]; - sprintf(code_str, "%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5]); - - bitbuffer_invert(bitbuffer); - - int id = (b[0] << 8) | b[1]; - if (id == 0xffff) { - return DECODE_ABORT_EARLY; - } - - int event_type = b[2] & 0x0f; - - int event = (b[2] << 8) | b[3]; - if (event == 0xffff) { - return DECODE_ABORT_EARLY; - } - - decoder_logf(decoder, 1, __func__, "Original Bytes: %02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5]); - - uint8_t parity = (b[5] >> 1 & 0x0F); // Shift 101PPPP1 -> 0101PPPP, then and with 0x0F so we're left with 000PPPP - - decoder_logf(decoder, 1, __func__, "Parity: %02x", parity); - - int chk = xor_bytes(b, 5); - chk = (chk >> 4) ^ (chk & 0xf); - - // Parity arguments were discovered using revdgst's RevSum and the data packets included at the top of this file. - // https://github.com/triq-org/revdgst - if (chk != parity) { - decoder_log(decoder, 1, __func__, "Parity did NOT match."); - return DECODE_FAIL_MIC; - } - - // Only valid for event nibble 0xc - // voltage fit value from 8 different sensor units, observed 2 to 3.1 volts - int battery = event_type == 0xc ? b[3] : 0; // percentage gauge - float battery_level = battery * 0.01f; - int battery_mv = 1800 + 12 * battery; - - // Strip off the upper nibble - event &= 0x0FFF; - - char const *event_str; - // Figure out what event was triggered - if (event == 0xafa) { - event_str = "Button Press"; - } - else if (event == 0xbfb) { - event_str = "Water Leak"; - } - else if (event_type == 0xc) { - event_str = "Battery Report"; - } - else if (event == 0xdfd) { - event_str = "Heartbeat"; - } - else if (event == 0xe7f) { - // Only sent by the Contact sensor - model_num = GOVEE_CONTACT; - event_str = "Open"; - } - else { - event_str = "Unknown"; - } - - /* clang-format off */ - data_t *data = data_make( - "model", "", DATA_COND, model_num == GOVEE_WATER, DATA_STRING, "Govee-Water", - "model", "", DATA_COND, model_num == GOVEE_CONTACT, DATA_STRING, "Govee-Contact", - "id" , "", DATA_INT, id, - "battery_ok", "Battery level", DATA_COND, battery, DATA_DOUBLE, battery_level, - "battery_mV", "Battery", DATA_COND, battery, DATA_FORMAT, "%d mV", DATA_INT, battery_mv, - "event", "", DATA_STRING, event_str, - "code", "Raw Code", DATA_STRING, code_str, - "mic", "Integrity", DATA_STRING, "PARITY", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - - return 1; -} - -static char const *const output_fields[] = { - "model", - "id", - "battery_ok", - "battery_mV", - "event", - "code", - "mic", - NULL, -}; - -r_device const govee = { - .name = "Govee Water Leak Detector H5054, Door Contact Sensor B5023", - .modulation = OOK_PULSE_PWM, - .short_width = 440, // Threshold between short and long pulse [us] - .long_width = 940, // Maximum gap size before new row of bits [us] - .gap_limit = 900, // Maximum gap size before new row of bits [us] - .reset_limit = 9000, // Maximum gap size before End Of Message [us] - .decode_fn = &govee_decode, - .fields = output_fields, -}; - -/** -Govee Water Leak Detector H5054 - -This is an updated decoder for devices with board versions dated circa 2021 as originally -reported in issue #2265. - -Data layout: - - II II XE DD CC CC - -- I: 16 bit ID, does not change with battery change -- X: 4 bit, always 0x3 for the sensors evaluated -- E: 4 bit event type -- D: 8 bit event data -- C: CRC-16/AUG-CCITT, poly=0x1021, init=0x1d0f - - -Event Information: - -- 0x0 : Button Press - - The event data (DD) is always 0x54 for the sensors evaluated. Unknown meaning. -- 0x1 : Battery Report - - The event data (DD) reported for new batteries = 0x64 (decimal 100). When inserting - older batteries, this value decreased. Looking at prior versions of the device, - this appears to be a battery level percentage. -- 0x2 = Water Leak - - The event data (DD) reported appears to be an incrementing counter for the event - number. This value is reset to 00 when new batteries are inserted. - - When the first leak occurs, E=2 D=00. This value is transmitted once very 5 seconds - until the leak is cleared (sensor dried off). The next leak events will be: - - E=2, D=01 - E=2, D=02 - E=2, D=03 - etc... - -CRC Information: - -The CRC was determined by using the tool CRC RevEng: https://reveng.sourceforge.io/: - - ./reveng -w16 -s aaaaaaaaaaaa bbbbbbbbbbbb etc... - -where aaaaaaaaaaaa, bbbbbbbbbbbb, etc... were the unique codes collected from the -device. - -*/ - -typedef enum { - GOVEE_BUTTON_PRESS = 0, - GOVEE_BATTERY_REPORT = 1, - GOVEE_WATER_LEAK = 2, -} govee_h5054_event_t; - -static int govee_h5054_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - if (bitbuffer->num_rows < 3) { - return DECODE_ABORT_EARLY; - } - - int r = bitbuffer_find_repeated_row(bitbuffer, 3, GOVEE_H5054_BITLEN); - if (r < 0) { - return DECODE_ABORT_EARLY; - } - - if (bitbuffer->bits_per_row[r] > GOVEE_H5054_BITLEN) { - return DECODE_ABORT_LENGTH; - } - - bitbuffer_invert(bitbuffer); - - uint8_t *b = bitbuffer->bb[r]; - - char code_str[13]; - sprintf(code_str, "%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5]); - - uint16_t chk = crc16(b, 6, 0x1021, 0x1d0f); - if (chk != 0) { - return DECODE_FAIL_MIC; - } - - const uint16_t id = b[0] << 8 | b[1]; - const uint8_t unk16 = (b[2] & 0xf0) >> 4; - const uint8_t event = b[2] & 0xf; - const uint8_t event_data = b[3]; - const uint16_t crc_sum = b[4] << 8 | b[5]; - - decoder_logf(decoder, 1, __func__, "Original Bytes: %02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5]); - decoder_logf(decoder, 1, __func__, "id=%04x", id); - decoder_logf(decoder, 1, __func__, "unk16=%x", unk16); - decoder_logf(decoder, 1, __func__, "event=%x", event); - decoder_logf(decoder, 1, __func__, "event_data=%02x", event_data); - decoder_logf(decoder, 1, __func__, "crc_sum=%04x", crc_sum); - - char const *event_str; - int leak_num = -1; - int battery = -1; - switch (event) { - case GOVEE_BUTTON_PRESS: - event_str = "Button Press"; - break; - case GOVEE_BATTERY_REPORT: - event_str = "Battery Report"; - battery = event_data; - break; - case GOVEE_WATER_LEAK: - event_str = "Water Leak"; - leak_num = event_data; - break; - default: - event_str = "Unknown"; - break; - } - - float battery_level = battery * 0.01f; - int battery_mv = 1800 + 12 * battery; - - /* clang-format off */ - data_t *data = data_make( - "model", "", DATA_STRING, "Govee-Water", - "id" , "", DATA_INT, id, - "battery_ok", "Battery level", DATA_COND, battery >= 0, DATA_DOUBLE, battery_level, - "battery_mV", "Battery", DATA_COND, battery >= 0, DATA_FORMAT, "%d mV", DATA_INT, battery_mv, - "event", "", DATA_STRING, event_str, - "leak_num", "Leak Num", DATA_COND, leak_num >= 0, DATA_INT, leak_num, - "code", "Raw Code", DATA_STRING, code_str, - "mic", "Integrity", DATA_STRING, "CRC", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - - return 1; -} - -r_device const govee_h5054 = { - .name = "Govee Water Leak Detector H5054", - .modulation = OOK_PULSE_PWM, - .short_width = 440, // Threshold between short and long pulse [us] - .long_width = 940, // Maximum gap size before new row of bits [us] - .gap_limit = 900, // Maximum gap size before new row of bits [us] - .reset_limit = 9000, // Maximum gap size before End Of Message [us] - .decode_fn = &govee_h5054_decode, - .fields = output_fields, -}; diff --git a/contrib/interlogix.c b/contrib/interlogix.c deleted file mode 100644 index 3e8991c9..00000000 --- a/contrib/interlogix.c +++ /dev/null @@ -1,239 +0,0 @@ -/** @file - Interlogix/GE/UTC Wireless Device Decoder. - - Copyright (C) 2017 Brent Bailey - - 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. -*/ -/** -Interlogix/GE/UTC Wireless Device Decoder. - -Also tested with ELK-319DWM module as well as a Alula RE101 319.5MHz sensor (both short preamble). - -- Frequency: 319.5 MHz - -Decoding done per us patent #5761206 -https://www.google.com/patents/US5761206 - -## Protocol Bits - -- 00-02 976 uS RF front porch pulse -- 03-14 12 sync pulses, logical zeros -- 15 start pulse, logical one -- 16-35 20 bit sensor identification code (ID bits 0-19) -- 36-39 4 bit device type code (DT bits 0-3) -- 40-42 3 bit trigger count (TC bit 0-2) -- 43 low battery bit -- 44 F1 latch bit NOTE that F1 latch bit and debounce are reversed. Typo or endianness issue? -- 45 F1 debounced level -- 46 F2 latch bit -- 47 F2 debounced level -- 48 F3 latch bit (cover latch for contact sensors) -- 49 F3 debounced level -- 50 F4 latch bit -- 51 F4 debounced level -- 52 F5 positive latch bit -- 53 F5 debounced level -- 54 F5 negative latch bit -- 55 even parity over odd bits 15-55 -- 56 odd parity over even bits 16-56 -- 57 zero/one, programmable -- 58 RF on for 366 uS (old stop bit) -- 59 one -- 60-62 modulus 8 count of number of ones in bits 15-54 -- 63 zero (new stop bit) - -## Protocol Description - -- Bits 00 to 02 are a 976 ms RF front porch pulse, providing a wake up period that allows the - system controller receiver to synchronize with the incoming packet. -- Bits 3 to 14 include 12 sync pulses, e.g., logical 0's, to synchronize the receiver. -- Bit 15 is a start pulse, e.g., a logical 1, that tells the receiver that data is to follow. -- Bits 16-58 provide information regarding the transmitter and associated sensor. In other - embodiments, bits 16-58 may be replaced by an analog signal. -- Bits 16 to 35 provide a 20-bit sensor identification code that uniquely identifies the particular - sensor sending the message. Bits 36 to 39 provide a 4 bit device-type code that identifies the - specific-type of sensor, e.g., smoke, PIR, door, window, etc. The combination of the sensor - bits and device bits provide a set of data bits. -- Bits 40 through 42 provide a 3-bit trigger count that is incremented for each group of message - packets. The trigger count is a simple but effective way for preventing a third party from - recording a message packet transmission and then re-transmitting that message packet - transmission to make the system controller think that a valid message packet is being transmitted. -- Bit 43 provides the low battery bit. -- Bits 44 through 53 provide the latch bit value and the debounced value for each of the five inputs - associated with the transmitter. For the F5 input, both a positive and negative latch bit are provided. -- Bit 55 provides even parity over odd bits 15 to 55. -- Bit 56 provides odd parity over even bits 16 to 56. -- Bit 57 is a programmable bit that can be used for a variety of applications, including providing an - additional bit that could be used for the sensor identification code or device type code. -- Bit 58 is a 366 ms RF on signal that functions as the "old" stop bit. This bit provides compatibility with - prior system controllers that may be programmed to receive a 58-bit message. -- Bit 59 is a logical 1. -- Bits 60 to 62 are a modulus eight count of the number of 1 bits in bits 15 through 54, providing enhanced - error detection information to be used by the system controller. Finally, bit 63 is the "new" stop bit, - e.g., a logical 0, that tells the system controller that it is the end of the message packet. - -## Addendum - -GE/Interlogix keyfobs do not follow the documented iti protocol and it -appears the protocol was misread by the team that created the keyfobs. -The button states are sent in the three trigger count bits (bit 40-42) -and no battery status appears to be provided. 4 buttons and a single -multi-button press (buttons 1 - lock and buttons 2 - unlock) for a total -of 5 buttons available on the keyfob. - -For contact sensors, latch 3 (typically the tamper/case open latch) will -float (giving misreads) if the external contacts are used (ie; closed) -and there is no 4.7 Kohm end of line resistor in place on the external -circuit -*/ - -#include "decoder.h" - -#define INTERLOGIX_MSG_BIT_LEN 46 - -static int interlogix_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - // preamble message - // only searching for 0000 0001 (bottom 8 bits of the 13 bits preamble) - uint8_t const preamble_pattern[1] = {0x01}; // 8 bits - - data_t *data; - unsigned int row = 0; - char device_type_id[2]; - char const *device_type; - char device_serial[7]; - char raw_message[7]; - int low_battery; - char const *f1_latch_state; - char const *f2_latch_state; - char const *f3_latch_state; - char const *f4_latch_state; - char const *f5_latch_state; - - if (bitbuffer->num_rows != 1) { - return DECODE_ABORT_EARLY; - } - - // Check if the message length is between the length seen in test files (57) - // and the 64 bits discussed above. - if (bitbuffer->bits_per_row[0] < 57 - || bitbuffer->bits_per_row[0] > 64) { - return DECODE_ABORT_LENGTH; - } - - // search for preamble and exit if not found - unsigned int bit_offset = bitbuffer_search(bitbuffer, row, 0, preamble_pattern, (sizeof preamble_pattern) * 8); - if (bit_offset == bitbuffer->bits_per_row[row]) { - decoder_logf(decoder, 2, __func__, "Preamble not found, bit_offset: %u", bit_offset); - return DECODE_FAIL_SANITY; - } - - // set message starting position (just past preamble and sync bit) - bit_offset += (sizeof preamble_pattern) * 8; - - uint8_t message[(INTERLOGIX_MSG_BIT_LEN + 7) / 8]; - - bitbuffer_extract_bytes(bitbuffer, row, bit_offset, message, INTERLOGIX_MSG_BIT_LEN); - - // reduce false positives, abort if id or code looks wrong - if (message[0] == 0x00 && message[1] == 0x00 && message[2] == 0x00) - return DECODE_FAIL_SANITY; - if (message[0] == 0xff && message[1] == 0xff && message[2] == 0xff) - return DECODE_FAIL_SANITY; - if (message[3] == 0x00 && message[4] == 0x00 && message[5] == 0x00) - return DECODE_FAIL_SANITY; - if (message[3] == 0xff && message[4] == 0xff && message[5] == 0xff) - return DECODE_FAIL_SANITY; - - // parity check: even data bits from message[0 .. 40] and odd data bits from message[1 .. 41] - // i.e. 5 bytes and two (top-most) bits. - int parity = message[0] ^ message[1] ^ message[2] ^ message[3] ^ message[4]; // parity as byte - parity = (parity >> 4) ^ (parity & 0xF); // fold to nibble - parity = (parity >> 2) ^ (parity & 0x3); // fold to 2 bits - parity ^= message[5] >> 6; // add check bits - int parity_error = parity ^ 0x3; // both parities are odd, i.e. 1 on success - - if (parity_error) { - decoder_logf(decoder, 1, __func__, "Parity check failed (%d %d)", parity >> 1, parity & 1); - return DECODE_FAIL_MIC; - } - - sprintf(device_type_id, "%01x", (reverse8(message[2]) >> 4)); - - switch ((reverse8(message[2]) >> 4)) { - case 0xa: device_type = "contact"; break; - case 0xf: device_type = "keyfob"; break; - case 0x4: device_type = "motion"; break; - case 0x6: device_type = "heat"; break; - case 0x9: device_type = "glass"; break; // switch1 changes from open to closed on trigger - - default: device_type = "unknown"; return DECODE_FAIL_SANITY; break; - } - - sprintf(device_serial, "%02x%02x%02x", reverse8(message[2]), reverse8(message[1]), reverse8(message[0])); - - sprintf(raw_message, "%02x%02x%02x", message[3], message[4], message[5]); - - // keyfob logic. see protocol description addendum for protocol exceptions - if ((reverse8(message[2]) >> 4) == 0xf) { - low_battery = 0; - f1_latch_state = ((message[3] & 0xe) == 0x4) ? "CLOSED" : "OPEN"; - f2_latch_state = ((message[3] & 0xe) == 0x8) ? "CLOSED" : "OPEN"; - f3_latch_state = ((message[3] & 0xe) == 0xc) ? "CLOSED" : "OPEN"; - f4_latch_state = ((message[3] & 0xe) == 0x2) ? "CLOSED" : "OPEN"; - f5_latch_state = ((message[3] & 0xe) == 0xa) ? "CLOSED" : "OPEN"; - } else { - low_battery = (message[3] & 0x10) ? 1 : 0; - f1_latch_state = (message[3] & 0x04) ? "OPEN" : "CLOSED"; - f2_latch_state = (message[3] & 0x01) ? "OPEN" : "CLOSED"; - f3_latch_state = (message[4] & 0x40) ? "OPEN" : "CLOSED"; - f4_latch_state = (message[4] & 0x10) ? "OPEN" : "CLOSED"; - f5_latch_state = (message[4] & 0x04) ? "OPEN" : "CLOSED"; - } - - /* clang-format off */ - data = data_make( - "model", "Model", DATA_STRING, "Interlogix-Security", - "subtype", "Device Type", DATA_STRING, device_type, - "id", "ID", DATA_STRING, device_serial, - "battery_ok", "Battery", DATA_INT, !low_battery, - "switch1", "Switch1 State", DATA_STRING, f1_latch_state, - "switch2", "Switch2 State", DATA_STRING, f2_latch_state, - "switch3", "Switch3 State", DATA_STRING, f3_latch_state, - "switch4", "Switch4 State", DATA_STRING, f4_latch_state, - "switch5", "Switch5 State", DATA_STRING, f5_latch_state, - "raw_message", "Raw Message", DATA_STRING, raw_message, - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - return 1; -} - -static char const *const output_fields[] = { - "model", - "subtype", - "id", - "raw_message", - "battery_ok", - "switch1", - "switch2", - "switch3", - "switch4", - "switch5", - NULL, -}; - -r_device const interlogix = { - .name = "Interlogix GE UTC Security Devices", - .modulation = OOK_PULSE_PPM, - .short_width = 122, - .long_width = 244, - .reset_limit = 500, // Maximum gap size before End Of Message - .decode_fn = &interlogix_decode, - .fields = output_fields, -}; diff --git a/contrib/lacrosse_tx141x.c b/contrib/lacrosse_tx141x.c deleted file mode 100644 index d870be9f..00000000 --- a/contrib/lacrosse_tx141x.c +++ /dev/null @@ -1,331 +0,0 @@ -/** @file - LaCrosse TX141-Bv2, TX141TH-Bv2, TX141-Bv3, TX145wsdth sensor. - - Changes done by Andrew Rivett . Copyright is - retained by Robert Fraczkiewicz. - - Copyright (C) 2017 Robert Fraczkiewicz - - 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. -*/ -/** -LaCrosse TX141-Bv2, TX141TH-Bv2, TX141-Bv3, TX145wsdth sensor. - -Also TFA 30.3221.02 (a TX141TH-Bv2), -also TFA 30.3222.02 (a LaCrosse-TX141W). -also TFA 30.3251.10 (a LaCrosse-TX141W). -also some rebrand (ORIA WA50B) with a slightly longer timing, s.a. #2088 -also TFA 30.3243.02 (a LaCrosse-TX141Bv3) - -LaCrosse Color Forecast Station (model C85845), or other LaCrosse product -utilizing the remote temperature/humidity sensor TX141TH-Bv2 transmitting -in the 433.92 MHz band. Product pages: -http://www.lacrossetechnology.com/c85845-color-weather-station/ -http://www.lacrossetechnology.com/tx141th-bv2-temperature-humidity-sensor - -The TX141TH-Bv2 protocol is OOK modulated PWM with fixed period of 625 us -for data bits, preambled by four long startbit pulses of fixed period equal -to ~1666 us. Hence, it is similar to Bresser Thermo-/Hygro-Sensor 3CH. - -A single data packet looks as follows: -1) preamble - 833 us high followed by 833 us low, repeated 4 times: - - ---- ---- ---- ---- - | | | | | | | | - ---- ---- ---- ---- - -2) a train of 40 data pulses with fixed 625 us period follows immediately: - - --- -- -- --- --- -- --- - | | | | | | | | | | | | | | - -- --- --- -- -- --- -- .... - -A logical 1 is 417 us of high followed by 208 us of low. -A logical 0 is 208 us of high followed by 417 us of low. -Thus, in the example pictured above the bits are 1 0 0 1 1 0 1 .... - -The TX141TH-Bv2 sensor sends 12 of identical packets, one immediately following -the other, in a single burst. These 12-packet bursts repeat every 50 seconds. At -the end of the last packet there are two 833 us pulses ("post-amble"?). - -The TX141-Bv3 has a revision which only sends 4 packets per transmission. - -The data is grouped in 5 bytes / 10 nybbles - - [id] [id] [flags] [temp] [temp] [temp] [humi] [humi] [chk] [chk] - -- id: 8 bit random integer generated at each powers up -- flags: 4 bit for battery low indicator, test button press, and channel -- temp: 12 bit unsigned temperature in degrees Celsius, scaled by 10, offset 500, range -40 C to 60 C -- humi: 8 bit integer indicating relative humidity in %. -- chk: 8 bit checksum is a digest, 0x31, 0xf4, reflected - -A count enables us to determine the quality of radio transmission. - -*** Addition of TX141 temperature only device, Jan 2018 by Andrew Rivett ** - -The TX141-BV2 is the temperature only version of the TX141TH-BV2 sensor. - -Changes: -- Changed minimum bit length to 32 (tx141b is temperature only) -- LACROSSE_TX141_BITLEN is 37 instead of 40. -- The humidity variable has been removed for TX141. -- Battery check bit is inverse of TX141TH. -- temp_f removed, temp_c (celsius) is what's provided by the device. - -- TX141TH-BV3 bitlen is 41 - -Addition of TX141W and TX145wsdth: - - PRE5b ID19h BAT1b TEST?1b CH?2h TYPE4h TEMP_WIND12d HUM_DIR12d CHK8h 1x - -- type 1 has temp+hum (temp is offset 500 and scale 10) -- type 2 has wind speed (km/h scale 10) and direction (degrees) -- checksum is CRC-8 poly 0x31 init 0x00 over preceding 7 bytes - -*/ - -#include "decoder.h" - -// Define the types of devices this file supports (uses expected bitlen) -#define LACROSSE_TX141B 32 -#define LACROSSE_TX141 37 -#define LACROSSE_TX141TH 40 -#define LACROSSE_TX141BV3 33 -#define LACROSSE_TX141W 65 - -static int lacrosse_tx141x_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - data_t *data; - int r; - int device; - uint8_t *b; - - // Find the most frequent data packet - // reduce false positives, require at least 5 out of 12 or 3 of 4 repeats. - // allows 4-repeats transmission to contain a bogus extra row. - r = bitbuffer_find_repeated_row(bitbuffer, bitbuffer->num_rows > 5 ? 5 : 3, 32); // 32 - if (r < 0) { - // try again for TX141W/TX145wsdth, require at least 2 out of 3-7 repeats. - r = bitbuffer_find_repeated_row(bitbuffer, 2, 64); // 65 - } - - bitbuffer_invert(bitbuffer); - - // For LACROSSE_TX141TH, do not require duplicate packets because it contains a CRC - // that is checked indepently. However, to simplify the code below, introduce a shortcut - // here instead of restructuring all following code: pre-select a row that fulfills - // protocol requirements (num_rows, bits_per_row and CRC) here and keep other sanity checks - // to the existing code below. - if (bitbuffer->num_rows <= 4) { - for (int row = 0; row < bitbuffer->num_rows; row++) { - if ((bitbuffer->bits_per_row[row] == 40 || bitbuffer->bits_per_row[row] == 41) && - lfsr_digest8_reflect(bitbuffer->bb[row], 4, 0x31, 0xf4) == bitbuffer->bb[row][4]) { - r = row; - } - } - } - - if (r < 0) { - return DECODE_ABORT_LENGTH; - } - - if (bitbuffer->bits_per_row[r] >= 64) { - device = LACROSSE_TX141W; - } - else if (bitbuffer->bits_per_row[r] > 41) { - return DECODE_ABORT_LENGTH; - } - else if (bitbuffer->bits_per_row[r] >= 41) { - if (bitbuffer->num_rows > 12) { - return DECODE_ABORT_LENGTH; // false-positive with GT-WT03 - } - device = LACROSSE_TX141TH; // actually TX141TH-BV3 - } - else if (bitbuffer->bits_per_row[r] >= 40) { - device = LACROSSE_TX141TH; - } - else if (bitbuffer->bits_per_row[r] >= 37) { - device = LACROSSE_TX141; - } - else if (bitbuffer->bits_per_row[r] == 32) { - device = LACROSSE_TX141B; - } else { - device = LACROSSE_TX141BV3; - } - - b = bitbuffer->bb[r]; - - if (device == LACROSSE_TX141W) { - int pre = (b[0] >> 3); - if (pre != 0x01) { - return DECODE_ABORT_EARLY; - } - - int chk = crc8(b, 8, 0x31, 0x00); - if (chk) { - return DECODE_FAIL_MIC; - } - - int id = ((b[0] & 0x07) << 16) | (b[1] << 8) | b[2]; - int battery_low = (b[3] >> 7); - int test = (b[3] & 0x40) >> 6; - int channel = (b[3] & 0x30) >> 4; - int type = (b[3] & 0x0f); - int temp_raw = (b[4] << 4) | (b[5] >> 4); - int humidity = ((b[5] & 0x0f) << 8) | b[6]; - - if (type == 1) { - // Temp/Hum - float temp_c = (temp_raw - 500) * 0.1f; - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "LaCrosse-TX141W", - "id", "Sensor ID", DATA_FORMAT, "%05x", DATA_INT, id, - "channel", "Channel", DATA_FORMAT, "%01x", DATA_INT, channel, - "battery_ok", "Battery level", DATA_INT, !battery_low, - "temperature_C", "Temperature", DATA_FORMAT, "%.2f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - "test", "Test?", DATA_INT, test, - "mic", "Integrity", DATA_STRING, "CRC", - NULL); - /* clang-format on */ - } - else if (type == 2) { - // Wind - float speed_kmh = temp_raw * 0.1f; - // wind direction is in humidity field - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "LaCrosse-TX141W", - "id", "Sensor ID", DATA_FORMAT, "%05x", DATA_INT, id, - "channel", "Channel", DATA_FORMAT, "%01x", DATA_INT, channel, - "battery_ok", "Battery level", DATA_INT, !battery_low, - "wind_avg_km_h", "Wind speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh, - "wind_dir_deg", "Wind direction", DATA_INT, humidity, - "test", "Test?", DATA_INT, test, - "mic", "Integrity", DATA_STRING, "CRC", - NULL); - /* clang-format on */ - } - else { - decoder_logf(decoder, 1, __func__, "unknown subtype: %d", type); - return DECODE_FAIL_OTHER; - } - - decoder_output_data(decoder, data); - return 1; - } - - int id = b[0]; - int battery_low; - if (device == LACROSSE_TX141TH) { - battery_low = (b[1] >> 7); - } - else { // LACROSSE_TX141 || LACROSSE_TX141BV3 - battery_low = !(b[1] >> 7); - } - int test = (b[1] & 0x40) >> 6; - int channel = (b[1] & 0x30) >> 4; - int temp_raw = ((b[1] & 0x0F) << 8) | b[2]; - float temp_c = (temp_raw - 500) * 0.1f; // Temperature in C - - int humidity = 0; - if (device == LACROSSE_TX141TH) { - humidity = b[3]; - } - - if (0 == id || (device == LACROSSE_TX141TH && (0 == humidity || humidity > 100)) || temp_c < -40.0 || temp_c > 140.0) { - decoder_logf(decoder, 1, __func__, "data error, id: %i, humidity:%i, temp:%f", id, humidity, temp_c); - return DECODE_FAIL_SANITY; - } - - if (device == LACROSSE_TX141B) { - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "LaCrosse-TX141B", - "id", "Sensor ID", DATA_FORMAT, "%02x", DATA_INT, id, - "temperature_C", "Temperature", DATA_FORMAT, "%.2f C", DATA_DOUBLE, temp_c, - "battery_ok", "Battery", DATA_INT, !battery_low, - "test", "Test?", DATA_STRING, test ? "Yes" : "No", - NULL); - /* clang-format on */ - } else if (device == LACROSSE_TX141) { - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "LaCrosse-TX141Bv2", - "id", "Sensor ID", DATA_FORMAT, "%02x", DATA_INT, id, - "channel", "Channel", DATA_INT, channel, - "temperature_C", "Temperature", DATA_FORMAT, "%.2f C", DATA_DOUBLE, temp_c, - "battery_ok", "Battery", DATA_INT, !battery_low, - "test", "Test?", DATA_STRING, test ? "Yes" : "No", - NULL); - /* clang-format on */ - } - else if (device == LACROSSE_TX141BV3) { - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "LaCrosse-TX141Bv3", - "id", "Sensor ID", DATA_FORMAT, "%02x", DATA_INT, id, - "channel", "Channel", DATA_INT, channel, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_C", "Temperature", DATA_FORMAT, "%.2f C", DATA_DOUBLE, temp_c, - "test", "Test?", DATA_STRING, test ? "Yes" : "No", - NULL); - /* clang-format on */ - } - else { - // Digest check for TX141TH-Bv2 - if (lfsr_digest8_reflect(b, 4, 0x31, 0xf4) != b[4]) { - decoder_logf(decoder, 1, __func__, "Checksum digest TX141TH failed"); - return DECODE_FAIL_MIC; - } - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "LaCrosse-TX141THBv2", - "id", "Sensor ID", DATA_FORMAT, "%02x", DATA_INT, id, - "channel", "Channel", DATA_INT, channel, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_C", "Temperature", DATA_FORMAT, "%.2f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - "test", "Test?", DATA_STRING, test ? "Yes" : "No", - "mic", "Integrity", DATA_STRING, "CRC", - NULL); - /* clang-format on */ - } - - decoder_output_data(decoder, data); - return 1; -} - -static char const *output_fields[] = { - "model", - "id", - "channel", - "battery_ok", - "temperature_C", - "humidity", - "wind_avg_km_h", - "wind_dir_deg", - "test", - "mic", - NULL, -}; - -// note TX141W, TX145wsdth: m=OOK_PWM, s=256, l=500, r=1888, y=748 -r_device lacrosse_tx141x = { - .name = "LaCrosse TX141-Bv2, TX141TH-Bv2, TX141-Bv3, TX141W, TX145wsdth, (TFA, ORIA) sensor", - .modulation = OOK_PULSE_PWM, - .short_width = 208, // short pulse is 208 us + 417 us gap - .long_width = 417, // long pulse is 417 us + 208 us gap - .sync_width = 833, // sync pulse is 833 us + 833 us gap - .gap_limit = 625, // long gap (with short pulse) is ~417 us, sync gap is ~833 us - .reset_limit = 1700, // maximum gap is 1250 us (long gap + longer sync gap on last repeat) - .decode_fn = &lacrosse_tx141x_decode, - .fields = output_fields, -}; \ No newline at end of file diff --git a/contrib/oregon_scientific.c b/contrib/oregon_scientific.c deleted file mode 100644 index d16316db..00000000 --- a/contrib/oregon_scientific.c +++ /dev/null @@ -1,974 +0,0 @@ -/** @file - Various Oregon Scientific protocols. - - Copyright (C) 2015 Helge Weissig, Denis Bodor, Tommy Vestermark, Karl Lattimer, - deennoo, pclov3r, onlinux, Pasquale Fiorillo. - - 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. -*/ - -#include "decoder.h" - -/// Documentation for Oregon Scientific protocols can be found here: -/// http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf -// Sensors ID -#define ID_THGR122N 0x1d20 -#define ID_THGR968 0x1d30 -#define ID_BTHR918 0x5d50 -#define ID_BHTR968 0x5d60 -#define ID_RGR968 0x2d10 -#define ID_THR228N 0xec40 -#define ID_THN132N 0xec40 // same as THR228N but different packet size -#define ID_RTGN318 0x0cc3 // warning: id is from 0x0cc3 and 0xfcc3 -#define ID_RTGN129 0x0cc3 // same as RTGN318 but different packet size -#define ID_THGR810 0xf824 // This might be ID_THGR81, but what's true is lost in (git) history -#define ID_THGR810a 0xf8b4 // unconfirmed version -#define ID_THN802 0xc844 -#define ID_PCR800 0x2914 -#define ID_PCR800a 0x2d14 // Different PCR800 ID - AU version I think -#define ID_WGR800 0x1984 -#define ID_WGR800a 0x1994 // unconfirmed version -#define ID_WGR968 0x3d00 -#define ID_UV800 0xd874 -#define ID_THN129 0xcc43 // THN129 Temp only -#define ID_RTHN129 0x0cd3 // RTHN129 Temp, clock sensors -#define ID_BTHGN129 0x5d53 // Baro, Temp, Hygro sensor -#define ID_UVR128 0xec70 -#define ID_THGR328N 0xcc23 // Temp & Hygro sensor looks similar to THR228N but with 5 choice channel instead of 3 -#define ID_RTGR328N_1 0xdcc3 // RTGR328N_[1-5] RFclock (date &time) & Temp & Hygro sensor looks similar to THGR328N with RF clock (5 channels also) : Temp & hygro part -#define ID_RTGR328N_2 0xccc3 -#define ID_RTGR328N_3 0xbcc3 -#define ID_RTGR328N_4 0xacc3 -#define ID_RTGR328N_5 0x9cc3 -#define ID_RTGR328N_6 0x8ce3 // RTGR328N_6&7 RFclock (date &time) & Temp & Hygro sensor looks similar to THGR328N with RF clock (5 channels also) : RF Time part -#define ID_RTGR328N_7 0x8ae3 - -static float get_os_temperature(unsigned char *message) -{ - float temp_c = 0; - temp_c = (((message[5] >> 4) * 100) + ((message[4] & 0x0f) * 10) + ((message[4] >> 4) & 0x0f)) / 10.0F; - // Correct 0x0f to 0x08: - if (message[5] & 0x08) { - temp_c = -temp_c; - } - return temp_c; -} - -static float get_os_rain_rate(unsigned char *message) -{ - // Nibbles 11..8 rain rate, LSD = 0.1 units per hour, 4321 = 123.4 units per hour - float rain_rate = (((message[5] & 0x0f) * 1000) + ((message[5] >> 4) * 100) + ((message[4] & 0x0f) * 10) + (message[4] >> 4)) / 100.0F; - return rain_rate; -} - -static float get_os_total_rain(unsigned char *message) -{ - float total_rain = 0.0F; // Nibbles 17..12 Total rain, LSD = 0.001, 654321 = 123.456 - total_rain = (message[8] & 0x0f) * 100.0F - + ((message[8] >> 4) & 0x0f) * 10.0F + (message[7] & 0x0f) - + ((message[7] >> 4) & 0x0f) / 10.0F + (message[6] & 0x0f) / 100.0F - + ((message[6] >> 4) & 0x0f) / 1000.0F; - return total_rain; -} - -static unsigned int get_os_humidity(unsigned char *message) -{ - int humidity = 0; - humidity = ((message[6] & 0x0f) * 10) + (message[6] >> 4); - return humidity; -} - -static unsigned int get_os_uv(unsigned char *message) -{ - int uvidx = 0; - uvidx = ((message[4] & 0x0f) * 10) + (message[4] >> 4); - return uvidx; -} - -static unsigned int get_os_channel(unsigned char *message, unsigned int sensor_id) -{ - // sensor ID included to support sensors with channel in different position - int channel = 0; - channel = ((message[2] >> 4) & 0x0f); - if ((channel == 4) - && (sensor_id & 0x0fff) != ID_RTGN318 - && sensor_id != ID_THGR810 - && (sensor_id & 0x0fff) != ID_RTHN129 - && sensor_id != ID_THGR328N) - channel = 3; // sensor 3 channel number is 0x04 - return channel; -} - -static unsigned int get_os_battery(unsigned char *message) -{ - int battery_low = 0; - battery_low = (message[3] >> 2 & 0x01); - return battery_low; -} - -static unsigned int get_os_rollingcode(unsigned char *message) -{ - int rc = 0; - rc = (message[2] & 0x0F) + (message[3] & 0xF0); - return rc; -} - -static unsigned short int cm180i_power(uint8_t const *msg, unsigned int offset) -{ - unsigned short int val = 0; - - val = (msg[4+offset*2] << 8) | (msg[3+offset*2] & 0xF0); - // tested across situations varying from 700 watt to more than 8000 watt to - // get same value as showed in physical CM180 panel (exactly equals to 1+1/160) - val *= 1.00625; - return val; -} - -static unsigned long long cm180i_total(uint8_t const *msg) -{ - unsigned long long val = 0; - if ((msg[1] & 0x0F) == 0) { - // Sensor returns total only if nibble#4 == 0 - val = (unsigned long long)msg[14] << 40; - val += (unsigned long long)msg[13] << 32; - val += (unsigned long)msg[12] << 24; - val += (unsigned long)msg[11] << 16; - val += msg[10] << 8; - val += msg[9]; - } - return val; -} - -static uint8_t swap_nibbles(uint8_t byte) -{ - return (((byte&0xf) << 4) | (byte >> 4)); -} - -static unsigned short int cm180_power(uint8_t const *msg) -{ - unsigned short int val = 0; - val = (msg[4] << 8) | (msg[3] & 0xF0); - // tested across situations varying from 700 watt to more than 8000 watt to - // get same value as showed in physical CM180 panel (exactly equals to 1+1/160) - val *= 1.00625; - return val; -} - -static unsigned long long cm180_total(uint8_t const *msg) -{ - unsigned long long val = 0; - if ((msg[1] & 0x0F) == 0) { - // Sensor returns total only if nibble#4 == 0 - val = (unsigned long long)msg[10] << 40; - val += (unsigned long long)msg[9] << 32; - val += (unsigned long)msg[8] << 24; - val += (unsigned long)msg[7] << 16; - val += msg[6] << 8; - val += msg[5]; - } - return val; -} - -static int validate_os_checksum(r_device *decoder, unsigned char *msg, int checksum_nibble_idx) -{ - // Oregon Scientific v2.1 and v3 checksum is a 1 byte 'sum of nibbles' checksum. - // with the 2 nibbles of the checksum byte swapped. - int i; - unsigned int checksum, sum_of_nibbles = 0; - for (i = 0; i < checksum_nibble_idx - 1; i += 2) { - unsigned char val = msg[i >> 1]; - sum_of_nibbles += ((val >> 4) + (val & 0x0f)); - } - if (checksum_nibble_idx & 1) { - sum_of_nibbles += (msg[checksum_nibble_idx >> 1] >> 4); - checksum = (msg[checksum_nibble_idx >> 1] & 0x0f) | (msg[(checksum_nibble_idx + 1) >> 1] & 0xf0); - } - else { - checksum = (msg[checksum_nibble_idx >> 1] >> 4) | ((msg[checksum_nibble_idx >> 1] & 0x0f) << 4); - } - sum_of_nibbles &= 0xff; - - if (sum_of_nibbles == checksum) { - return 0; - } - else { - decoder_logf(decoder, 1, __func__, "Checksum error in Oregon Scientific message. Expected: %02x Calculated: %02x", checksum, sum_of_nibbles); - decoder_log_bitrow(decoder, 1, __func__, msg, ((checksum_nibble_idx + 4) >> 1) * 8, "Message"); - return 1; - } -} - -static int validate_os_v2_message(r_device *decoder, unsigned char *msg, int bits_expected, int msg_bits, - int nibbles_in_checksum) -{ - // Compare number of valid bits processed vs number expected - if (bits_expected == msg_bits) { - return validate_os_checksum(decoder, msg, nibbles_in_checksum); - } - decoder_logf_bitrow(decoder, 1, __func__, msg, msg_bits, "Bit validation error on Oregon Scientific message. Expected %d bits, Message", bits_expected); - return 1; -} - -/** -Various Oregon Scientific protocols. - -@todo Documentation needed. -*/ -static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - uint8_t *b = bitbuffer->bb[0]; - data_t *data; - - // Check 2nd and 3rd bytes of stream for possible Oregon Scientific v2.1 sensor data (skip first byte to get past sync/startup bit errors) - if (((b[1] != 0x55) || (b[2] != 0x55)) - && ((b[1] != 0xAA) || (b[2] != 0xAA))) { - if (b[3] != 0) { - decoder_log_bitrow(decoder, 1, __func__, b, bitbuffer->bits_per_row[0], "Badly formatted OS v2.1 message"); - } - return DECODE_ABORT_EARLY; - } - - bitbuffer_t databits = {0}; - uint8_t *msg = databits.bb[0]; - - // Possible v2.1 Protocol message - unsigned int sync_test_val = ((unsigned)b[3] << 24) | (b[4] << 16) | (b[5] << 8) | (b[6]); - // Could be extra/dropped bits in stream. Look for sync byte at expected position +/- some bits in either direction - for (int pattern_index = 0; pattern_index < 8; pattern_index++) { - unsigned int mask = (unsigned int)(0xffff0000 >> pattern_index); - unsigned int pattern = (unsigned int)(0x55990000 >> pattern_index); - unsigned int pattern2 = (unsigned int)(0xaa990000 >> pattern_index); - - decoder_logf(decoder, 1, __func__, "OS v2.1 sync byte search - test_val=%08x pattern=%08x mask=%08x", sync_test_val, pattern, mask); - - if (((sync_test_val & mask) != pattern) - && ((sync_test_val & mask) != pattern2)) - continue; // DECODE_ABORT_EARLY - - // Found sync byte - start working on decoding the stream data. - // pattern_index indicates where sync nibble starts, so now we can find the start of the payload - decoder_logf(decoder, 1, __func__, "OS v2.1 Sync test val %08x found, starting decode at bit %d", sync_test_val, pattern_index); - - //decoder_log_bitrow(decoder, 0, __func__, b, bitbuffer->bits_per_row[0], "Raw OSv2 bits"); - bitbuffer_manchester_decode(bitbuffer, 0, pattern_index + 40, &databits, 173); - reflect_nibbles(databits.bb[0], (databits.bits_per_row[0]+7)/8); - //decoder_logf_bitbuffer(decoder, 0, __func__, &databits, "MC OSv2 bits (from %d+40)", pattern_index); - - break; - } - int msg_bits = databits.bits_per_row[0]; - - int sensor_id = (msg[0] << 8) | msg[1]; - decoder_logf(decoder, 1, __func__,"Found sensor_id (%08x)",sensor_id); - if ((sensor_id == ID_THGR122N) || (sensor_id == ID_THGR968)) { - if (validate_os_v2_message(decoder, msg, 76, msg_bits, 15) != 0) - return 0; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, (sensor_id == ID_THGR122N) ? "Oregon-THGR122N" : "Oregon-THGR968", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, get_os_temperature(msg), - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_WGR968) { - if (validate_os_v2_message(decoder, msg, 94, msg_bits, 17) != 0) - return 0; - float quadrant = (msg[4] & 0x0f) * 10 + ((msg[4] >> 4) & 0x0f) * 1 + ((msg[5] >> 4) & 0x0f) * 100; - float avgWindspeed = ((msg[7] >> 4) & 0x0f) / 10.0F + (msg[7] & 0x0f) * 1.0F + ((msg[8] >> 4) & 0x0f) / 10.0F; - float gustWindspeed = (msg[5] & 0x0f) / 10.0F + ((msg[6] >> 4) & 0x0f) * 1.0F + (msg[6] & 0x0f) / 10.0F; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-WGR968", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "wind_max_m_s", "Gust", DATA_FORMAT, "%2.1f m/s",DATA_DOUBLE, gustWindspeed, - "wind_avg_m_s", "Average", DATA_FORMAT, "%2.1f m/s",DATA_DOUBLE, avgWindspeed, - "wind_dir_deg", "Direction", DATA_FORMAT, "%3.1f degrees",DATA_DOUBLE, quadrant, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_BHTR968) { - if (validate_os_v2_message(decoder, msg, 92, msg_bits, 19) != 0) - return 0; - //unsigned int comfort = msg[7] >> 4; - //char *comfort_str = "Normal"; - //if (comfort == 4) comfort_str = "Comfortable"; - //else if (comfort == 8) comfort_str = "Dry"; - //else if (comfort == 0xc) comfort_str = "Humid"; - //unsigned int forecast = msg[9] >> 4; - //char *forecast_str = "Cloudy"; - //if (forecast == 3) forecast_str = "Rainy"; - //else if (forecast == 6) forecast_str = "Partly Cloudy"; - //else if (forecast == 0xc) forecast_str = "Sunny"; - float temp_c = get_os_temperature(msg); - float pressure = ((msg[7] & 0x0f) | (msg[8] & 0xf0)) + 856; - // decoder_logf(decoder, 0, __func__,"Weather Sensor BHTR968 Indoor Temp: %3.1fC %3.1fF Humidity: %d%%", temp_c, ((temp_c*9)/5)+32, get_os_humidity(msg)); - // decoder_logf(decoder, 0, __func__, " (%s) Pressure: %dmbar (%s)", comfort_str, ((msg[7] & 0x0f) | (msg[8] & 0xf0))+856, forecast_str); - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-BHTR968", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), - "pressure_hPa", "Pressure", DATA_FORMAT, "%.0f hPa", DATA_DOUBLE, pressure, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_BTHR918) { - // Similar to the BHTR968, but smaller message and slightly different pressure offset - if (validate_os_v2_message(decoder, msg, 84, msg_bits, 19) != 0) - return 0; - float temp_c = get_os_temperature(msg); - float pressure = ((msg[7] & 0x0f) | (msg[8] & 0xf0)) + 795; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-BTHR918", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), - "pressure_hPa", "Pressure", DATA_FORMAT, "%.0f hPa", DATA_DOUBLE, pressure, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_RGR968) { - if (validate_os_v2_message(decoder, msg, 80, msg_bits, 16) != 0) - return 0; - float rain_rate = ((msg[4] & 0x0f) * 100 + (msg[4] >> 4) * 10 + ((msg[5] >> 4) & 0x0f)) / 10.0F; - float total_rain = ((msg[7] & 0xf) * 10000 + (msg[7] >> 4) * 1000 + (msg[6] & 0xf) * 100 + (msg[6] >> 4) * 10 + (msg[5] & 0xf)) / 10.0F; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-RGR968", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "rain_rate_mm_h", "Rain Rate", DATA_FORMAT, "%.02f mm/h", DATA_DOUBLE, rain_rate, - "rain_mm", "Total Rain", DATA_FORMAT, "%.02f mm", DATA_DOUBLE, total_rain, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_THR228N && msg_bits == 76) { - if (validate_os_v2_message(decoder, msg, 76, msg_bits, 12) != 0) - return 0; - float temp_c = get_os_temperature(msg); - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-THR228N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_THN132N && msg_bits == 64) { - if (validate_os_v2_message(decoder, msg, 64, msg_bits, 12) != 0) - return 0; - // Sanity check BCD digits - if (((msg[5] >> 4) & 0x0F) > 9 || (msg[4] & 0x0F) > 9 || ((msg[4] >> 4) & 0x0F) > 9) { - decoder_log(decoder, 1, __func__, "THN132N Message failed BCD sanity check."); - return DECODE_FAIL_SANITY; - } - float temp_c = get_os_temperature(msg); - // Sanity check value - if (temp_c > 70 || temp_c < -50) { - decoder_logf(decoder, 1, __func__, "THN132N Message failed values sanity check: temperature_C %3.1fC.", temp_c); - return DECODE_FAIL_SANITY; - } - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-THN132N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if ((sensor_id & 0x0fff) == ID_RTGN129 && msg_bits == 80) { - if (validate_os_v2_message(decoder, msg, 80, msg_bits, 15) != 0) - return 0; - float temp_c = get_os_temperature(msg); - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-RTGN129", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (((sensor_id == ID_RTGR328N_1) || (sensor_id == ID_RTGR328N_2) || (sensor_id == ID_RTGR328N_3) || (sensor_id == ID_RTGR328N_4) || (sensor_id == ID_RTGR328N_5)) && msg_bits == 173) { - if (validate_os_v2_message(decoder, msg, 173, msg_bits, 15) != 0) - return 0; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-RTGR328N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, get_os_temperature(msg), - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if ((sensor_id == ID_RTGR328N_6) || (sensor_id == ID_RTGR328N_7)) { - if (validate_os_v2_message(decoder, msg, 100, msg_bits, 21) != 0) - return 0; - - int year = ((msg[9] & 0x0F) * 10) + ((msg[9] & 0xF0) >> 4) + 2000; - int month = ((msg[8] & 0xF0) >> 4); - //int weekday = ((msg[8] & 0x0F)); - int day = ((msg[7] & 0x0F) * 10) + ((msg[7] & 0xF0) >> 4); - int hours = ((msg[6] & 0x0F) * 10) + ((msg[6] & 0xF0) >> 4); - int minutes = ((msg[5] & 0x0F) * 10) + ((msg[5] & 0xF0) >> 4); - int seconds = ((msg[4] & 0x0F) * 10) + ((msg[4] & 0xF0) >> 4); - - char clock_str[24]; - sprintf(clock_str, "%04d-%02d-%02dT%02d:%02d:%02d", - year, month, day, hours, minutes, seconds); - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-RTGR328N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "radio_clock", "Radio Clock", DATA_STRING, clock_str, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if ((sensor_id & 0x0fff) == ID_RTGN318) { - if (msg_bits == 76 && (validate_os_v2_message(decoder, msg, 76, msg_bits, 15) == 0)) { - float temp_c = get_os_temperature(msg); - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-RTGN318", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (msg_bits == 100 && (validate_os_v2_message(decoder, msg, 100, msg_bits, 21) == 0)) { - // RF Clock message ?? - return 0; - } - } - else if (sensor_id == ID_THN129 || (sensor_id & 0x0FFF) == ID_RTHN129) { - if ((validate_os_v2_message(decoder, msg, 68, msg_bits, 12) == 0)) { - float temp_c = get_os_temperature(msg); - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, (sensor_id == ID_THN129) ? "Oregon-THN129" : "Oregon-RTHN129", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (msg_bits == 104 && (validate_os_v2_message(decoder, msg, 104, msg_bits, 18) == 0)) { - // RF Clock message - return 0; - } - } - else if (sensor_id == ID_BTHGN129) { - if (validate_os_v2_message(decoder, msg, 92, msg_bits, 19) != 0) - return 0; - float temp_c = get_os_temperature(msg); - // Pressure is given in hPa. You may need to adjust the offset - // according to your altitude level (600 is a good starting point) - float pressure = ((msg[7] & 0x0f) | (msg[8] & 0xf0)) * 2 + (msg[8] & 0x01) + 600; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-BTHGN129", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), - "pressure_hPa", "Pressure", DATA_FORMAT, "%.02f hPa", DATA_DOUBLE, pressure, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_UVR128 && msg_bits == 148) { - if (validate_os_v2_message(decoder, msg, 148, msg_bits, 12) != 0) - return 0; - // Sanity check BCD digits - if (((msg[4] >> 4) & 0x0F) > 9 || (msg[4] & 0x0F) > 9) { - decoder_log(decoder, 1, __func__, "UVR128 Message failed BCD sanity check."); - return DECODE_FAIL_SANITY; - } - int uvidx = get_os_uv(msg); - // Sanity check value - if (uvidx < 0 || uvidx > 25) { - decoder_logf(decoder, 1, __func__, "UVR128 Message failed values sanity check: uv %u.", uvidx); - return DECODE_FAIL_SANITY; - } - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-UVR128", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "uv", "UV Index", DATA_FORMAT, "%u", DATA_INT, uvidx, - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - //"channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_THGR328N) { - if (validate_os_v2_message(decoder, msg, 173, msg_bits, 15) != 0) - return 0; - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-THGR328N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, get_os_temperature(msg), - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (msg_bits > 16) { - decoder_logf_bitrow(decoder, 1, __func__, msg, msg_bits, "Unrecognized Oregon Scientific v2.1 message (device ID %4x)", sensor_id); - } - else { - decoder_log_bitrow(decoder, 1, __func__, b, bitbuffer->bits_per_row[0], "Possible Oregon Scientific v2.1 message, but sync nibble wasn't found. Raw"); - } - - return 0; -} - -// ceil((335 + 11) / 8) -#define EXPECTED_NUM_BYTES 44 - -/** -Various Oregon Scientific protocols. - -@todo Documentation needed. -*/ -static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - uint8_t *b = bitbuffer->bb[0]; - data_t *data; - - // Check stream for possible Oregon Scientific v3 protocol preamble - if ((((b[0]&0xf) != 0x0f) || (b[1] != 0xff) || ((b[2]&0xc0) != 0xc0)) - && (((b[0]&0xf) != 0x00) || (b[1] != 0x00) || ((b[2]&0xc0) != 0x00))) { - if (b[3] != 0) { - decoder_log_bitrow(decoder, 1, __func__, b, bitbuffer->bits_per_row[0], "Unrecognized Msg in OS v3"); - } - return DECODE_ABORT_EARLY; - } - - unsigned char msg[EXPECTED_NUM_BYTES] = {0}; - int msg_pos = 0; - int msg_len = 0; - - // e.g. WGR800X has {335} 00 00 00 b1 22 40 0e 00 06 00 00 00 19 7c 00 00 00 b1 22 40 0e 00 06 00 00 00 19 7c 00 00 00 b1 22 40 0e 00 06 00 00 00 19 7c - // aligned (at 11) and reflected that's 3 packets: - // {324} 00 0a 19 84 00 e0 00 c0 00 00 00 3d 70 00 00 0a 19 84 00 e0 00 c0 00 00 00 3d 70 00 00 0a 19 84 00 e0 00 c0 00 00 00 3d 70 - - // full preamble is 00 00 00 5 (shorter for WGR800X) - uint8_t const os_pattern[] = {0x00, 0x05}; - // CM180 preamble is 00 00 00 46, with 0x46 already data - uint8_t const cm180_pattern[] = {0x00, 0x46}; - uint8_t const cm180i_pattern[] = {0x00, 0x4A}; - // workaround for a broken manchester demod - // CM160 preamble might look like 7f ff ff aa, i.e. ff ff f5 - uint8_t const alt_pattern[] = {0xff, 0xf5}; - - int os_pos = bitbuffer_search(bitbuffer, 0, 0, os_pattern, 16) + 16; - int cm180_pos = bitbuffer_search(bitbuffer, 0, 0, cm180_pattern, 16) + 8; // keep the 0x46 - int cm180i_pos = bitbuffer_search(bitbuffer, 0, 0, cm180i_pattern, 16) + 8; // keep the 0x46 - int alt_pos = bitbuffer_search(bitbuffer, 0, 0, alt_pattern, 16) + 16; - - if (bitbuffer->bits_per_row[0] - os_pos >= 7 * 8) { - msg_pos = os_pos; - msg_len = bitbuffer->bits_per_row[0] - os_pos; - } - - // 52 bits: secondary frame (instant watts only) - // 108 bits: primary frame (instant watts + cumulative wattshour) - else if (bitbuffer->bits_per_row[0] - cm180_pos >= 52) { - msg_pos = cm180_pos; - msg_len = bitbuffer->bits_per_row[0] - cm180_pos; - } - - else if (bitbuffer->bits_per_row[0] - cm180i_pos >= 84) { - msg_pos = cm180i_pos; - msg_len = bitbuffer->bits_per_row[0] - cm180i_pos; - } - - else if (bitbuffer->bits_per_row[0] - alt_pos >= 7 * 8) { - msg_pos = alt_pos; - msg_len = bitbuffer->bits_per_row[0] - alt_pos; - } - - if (msg_len == 0 || msg_len > (int)sizeof(msg) * 8) - return DECODE_ABORT_EARLY; - - bitbuffer_extract_bytes(bitbuffer, 0, msg_pos, msg, msg_len); - reflect_nibbles(msg, (msg_len + 7) / 8); - - int sensor_id = (msg[0] << 8) | msg[1]; - if (sensor_id == ID_THGR810 || sensor_id == ID_THGR810a) { - if (validate_os_checksum(decoder, msg, 15) != 0) - return DECODE_FAIL_MIC; - // Sanity check BCD digits - if (((msg[5] >> 4) & 0x0F) > 9 || (msg[4] & 0x0F) > 9 || ((msg[4] >> 4) & 0x0F) > 9 || (msg[6] & 0x0F) > 9 || ((msg[6] >> 4) & 0x0F) > 9) { - decoder_log(decoder, 1, __func__, "THGR810 Message failed BCD sanity check."); - return DECODE_FAIL_SANITY; - } - float temp_c = get_os_temperature(msg); - int humidity = get_os_humidity(msg); - // Sanity check values - if (temp_c > 70 || temp_c < -50 || humidity < 0 || humidity > 98) { - decoder_logf(decoder, 1, __func__, "THGR810 Message failed values sanity check: temperature_C %3.1fC humidity %d%%.", temp_c, humidity); - return DECODE_FAIL_SANITY; - } - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-THGR810", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; //msg[k] = ((msg[k] & 0x0F) << 4) + ((msg[k] & 0xF0) >> 4); - } - else if (sensor_id == ID_THN802) { - if (validate_os_checksum(decoder, msg, 12) != 0) - return DECODE_FAIL_MIC; - float temp_c = get_os_temperature(msg); - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-THN802", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_UV800) { - if (validate_os_checksum(decoder, msg, 13) != 0) - return DECODE_FAIL_MIC; - int uvidx = get_os_uv(msg); - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-UV800", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "uv", "UV Index", DATA_FORMAT, "%u", DATA_INT, uvidx, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_PCR800) { - if (validate_os_checksum(decoder, msg, 18) != 0) - return DECODE_FAIL_MIC; - // Sanity check BCD digits - if ((msg[8] & 0x0F) > 9 - || ((msg[8] >> 4) & 0x0F) > 9 - || (msg[7] & 0x0F) > 9 - || ((msg[7] >> 4) & 0x0F) > 9 - || (msg[6] & 0x0F) > 9 - || ((msg[6] >> 4) & 0x0F) > 9 - || (msg[5] & 0x0F) > 9 - || ((msg[5] >> 4) & 0x0F) > 9 - || (msg[4] & 0x0F) > 9 - || ((msg[4] >> 4) & 0x0F) > 9) { - decoder_log(decoder, 1, __func__, "PCR800 Message failed BCD sanity check."); - return DECODE_FAIL_SANITY; - } - - float rain_rate = get_os_rain_rate(msg); - float total_rain = get_os_total_rain(msg); - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-PCR800", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "rain_rate_in_h", "Rain Rate", DATA_FORMAT, "%5.1f in/h", DATA_DOUBLE, rain_rate, - "rain_in", "Total Rain", DATA_FORMAT, "%7.3f in", DATA_DOUBLE, total_rain, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_PCR800a) { - if (validate_os_checksum(decoder, msg, 18) != 0) - return DECODE_FAIL_MIC; - float rain_rate = get_os_rain_rate(msg); - float total_rain = get_os_total_rain(msg); - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-PCR800a", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "rain_rate_in_h", "Rain Rate", DATA_FORMAT, "%3.1f in/h", DATA_DOUBLE, rain_rate, - "rain_in", "Total Rain", DATA_FORMAT, "%3.1f in", DATA_DOUBLE, total_rain, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (sensor_id == ID_WGR800 || sensor_id == ID_WGR800a) { - if (validate_os_checksum(decoder, msg, 17) != 0) - return DECODE_FAIL_MIC; - // Sanity check BCD digits - if ((msg[5] & 0x0F) > 9 - || ((msg[6] >> 4) & 0x0F) > 9 - || (msg[6] & 0x0F) > 9 - || ((msg[7] >> 4) & 0x0F) > 9 - || (msg[7] & 0x0F) > 9 - || ((msg[8] >> 4) & 0x0F) > 9) { - decoder_log(decoder, 1, __func__, "WGR800 Message failed BCD sanity check."); - return DECODE_FAIL_SANITY; - } - - float gustWindspeed = (msg[5]&0x0f) /10.0F + ((msg[6]>>4)&0x0f) *1.0F + (msg[6]&0x0f) * 10.0F; - float avgWindspeed = ((msg[7]>>4)&0x0f) / 10.0F + (msg[7]&0x0f) *1.0F + ((msg[8]>>4)&0x0f) * 10.0F; - float quadrant = (0x0f&(msg[4]>>4))*22.5F; - - // Sanity check values - if (gustWindspeed < 0 || gustWindspeed > 56 || avgWindspeed < 0 || avgWindspeed > 56 || quadrant < 0 || quadrant > 337.5) { - decoder_logf(decoder, 1, __func__, "WGR800 Message failed values sanity check: wind_max_m_s %2.1f wind_avg_m_s %2.1f wind_dir_deg %3.1f.", gustWindspeed, avgWindspeed, quadrant); - return DECODE_FAIL_SANITY; - } - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-WGR800", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - "wind_max_m_s", "Gust", DATA_FORMAT, "%2.1f m/s",DATA_DOUBLE, gustWindspeed, - "wind_avg_m_s", "Average", DATA_FORMAT, "%2.1f m/s",DATA_DOUBLE, avgWindspeed, - "wind_dir_deg", "Direction", DATA_FORMAT, "%3.1f degrees",DATA_DOUBLE, quadrant, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if ((msg[0] == 0x20) || (msg[0] == 0x21) || (msg[0] == 0x22) || (msg[0] == 0x23) || (msg[0] == 0x24)) { // Owl CM160 Readings - msg[0] = msg[0] & 0x0F; - - if (validate_os_checksum(decoder, msg, 22) != 0) - return DECODE_FAIL_MIC; - - int id = msg[1] & 0x0F; - - unsigned int current_amps = swap_nibbles(msg[3]) | ((msg[4] >> 4) << 8); - double current_watts = current_amps * 0.07 * 230; // Assuming device is running in 230V country - - double total_amps = ((uint64_t)swap_nibbles(msg[10]) << 36) | ((uint64_t)swap_nibbles(msg[9]) << 28) | - (swap_nibbles(msg[8]) << 20) | (swap_nibbles(msg[7]) << 12) | - (swap_nibbles(msg[6]) << 4) | (msg[5]&0xf); - - double total_kWh = total_amps * 230.0 / 3600.0 / 1000.0 * 1.12; // Assuming device is running in 230V country - //result compares to the CM160 LCD display values when * 1.12 between readings - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-CM160", - "id", "House Code", DATA_INT, id, - // "current_A", "Current Amps", DATA_FORMAT, "%d A", DATA_INT, current_amps, - // "total_As", "Total Amps", DATA_FORMAT, "%d As", DATA_INT, (int)total_amps, - "power_W", "Power", DATA_FORMAT, "%7.4f W", DATA_DOUBLE, current_watts, - "energy_kWh", "Energy", DATA_FORMAT, "%7.4f kWh",DATA_DOUBLE, total_kWh, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - else if (msg[0] == 0x26) { // Owl CM180 readings - msg[0] = msg[0] & 0x0f; - int valid = validate_os_checksum(decoder, msg, 23); - for (int k = 0; k < EXPECTED_NUM_BYTES; k++) { // Reverse nibbles - msg[k] = (msg[k] & 0xF0) >> 4 | (msg[k] & 0x0F) << 4; - } - // TODO: should we return if valid == 0? - - int sequence = msg[1] & 0x0F; - int id = msg[2] << 8 | (msg[1] & 0xF0); - int batt_low = (msg[3] & 0x1); // 8th bit instead of 6th commonly used for other devices - - unsigned short int ipower = cm180_power(msg); - unsigned long long itotal = cm180_total(msg); - float total_energy = itotal / 3600.0 / 1000.0; - if (valid == 0) { - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-CM180", - "id", "House Code", DATA_INT, id, - "battery_ok", "Battery", DATA_INT, !batt_low, - "power_W", "Power", DATA_FORMAT, "%d W",DATA_INT, ipower, - "energy_kWh", "Energy", DATA_COND, itotal != 0, DATA_FORMAT, "%2.2f kWh",DATA_DOUBLE, total_energy, - "sequence", "sequence number", DATA_INT, sequence, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - } - else if (msg[0] == 0x25) { // Owl CM180i readings - int valid = 0; - msg[0] = msg[0] & 0x0f; - // to be done - // int valid = validate_os_checksum(decoder, msg, 23); - for (int k = 0; k < EXPECTED_NUM_BYTES; k++) { // Reverse nibbles - msg[k] = (msg[k] & 0xF0) >> 4 | (msg[k] & 0x0F) << 4; - } - // TODO: should we return if valid == 0? - - int sequence = msg[1] & 0x0F; - int id = msg[2] << 8 | (msg[1] & 0xF0); - int batt_low = (msg[3] & 0x40)?1:0; // 8th bit instead of 6th commonly used for other devices - - unsigned short int ipower1 = cm180i_power(msg,0); - unsigned short int ipower2 = cm180i_power(msg,1); - unsigned short int ipower3 = cm180i_power(msg,2); - unsigned long long itotal= 0; - if (msg_len >= 140) itotal= cm180i_total(msg); - - // per hour and in kilowat - float total_energy = itotal / 3600.0 / 1000.0; - - if (valid == 0) { - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Oregon-CM180i", - "id", "House Code", DATA_INT, id, - "battery_ok", "Battery", DATA_INT, !batt_low, - "power1_W", "Power1", DATA_FORMAT, "%d W",DATA_INT, ipower1, - "power2_W", "Power2", DATA_FORMAT, "%d W",DATA_INT, ipower2, - "power3_W", "Power3", DATA_FORMAT, "%d W",DATA_INT, ipower3, - "energy_kWh", "Energy", DATA_COND, itotal != 0, DATA_FORMAT, "%2.2f kWh",DATA_DOUBLE, total_energy, - "sequence", "sequence number", DATA_INT, sequence, - NULL); - /* clang-format on */ - decoder_output_data(decoder, data); - return 1; - } - } - else if ((msg[0] != 0) && (msg[1] != 0)) { // sync nibble was found and some data is present... - decoder_log(decoder, 1, __func__, "Message received from unrecognized Oregon Scientific v3 sensor."); - decoder_log_bitrow(decoder, 1, __func__, msg, msg_len, "Message"); - decoder_log_bitrow(decoder, 1, __func__, b, bitbuffer->bits_per_row[0], "Raw"); - } - else if (b[3] != 0) { - decoder_log(decoder, 1, __func__, "Possible Oregon Scientific v3 message, but sync nibble wasn't found"); - decoder_log_bitrow(decoder, 1, __func__, b, bitbuffer->bits_per_row[0], "Raw Data"); - } - return DECODE_FAIL_SANITY; -} - -/** -Various Oregon Scientific protocols. -@sa oregon_scientific_v2_1_decode() oregon_scientific_v3_decode() -*/ -static int oregon_scientific_decode(r_device *decoder, bitbuffer_t *bitbuffer) -{ - int ret = oregon_scientific_v2_1_decode(decoder, bitbuffer); - if (ret <= 0) - ret = oregon_scientific_v3_decode(decoder, bitbuffer); - return ret; -} - -static char const *const output_fields[] = { - "model", - "id", - "channel", - "battery_ok", - "temperature_C", - "humidity", - "rain_rate", // TODO: remove this - "rain_rate_mm_h", - "rain_rate_in_h", - "rain_total", // TODO: remove this - "rain_mm", - "rain_in", - "gust", // TODO: remove this - "average", // TODO: remove this - "direction", // TODO: remove this - "wind_max_m_s", - "wind_avg_m_s", - "wind_dir_deg", - "pressure_hPa", - "uv", - "power_W", - "energy_kWh", - "radio_clock", - "sequence", - NULL, -}; - -r_device const oregon_scientific = { - .name = "Oregon Scientific Weather Sensor", - .modulation = OOK_PULSE_MANCHESTER_ZEROBIT, - .short_width = 440, // Nominal 1024Hz (488us), but pulses are shorter than pauses - .long_width = 0, // not used - .reset_limit = 2400, - .decode_fn = &oregon_scientific_decode, - .fields = output_fields, -}; \ No newline at end of file diff --git a/include/data.h b/include/data.h index bb7486b8..976d44bc 100644 --- a/include/data.h +++ b/include/data.h @@ -21,71 +21,65 @@ #ifndef INCLUDE_DATA_H_ #define INCLUDE_DATA_H_ +// TODO: rtl_433_ESP #define _POSIX_HOST_NAME_MAX 128 #ifndef UINT_MAX -#define UINT_MAX 4294967295 +# define UINT_MAX 4294967295 #endif #if defined _WIN32 || defined __CYGWIN__ -#if defined data_EXPORTS -#define R_API \ - __stdcall __declspec( \ - dllexport) // Note: actually gcc seems to also supports this syntax. -#elif defined data_IMPORTS -#define R_API \ - __stdcall __declspec( \ - dllimport) // Note: actually gcc seems to also supports this syntax. +# if defined data_EXPORTS +# define R_API __stdcall __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. +# elif defined data_IMPORTS +# define R_API __stdcall __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. +# else +# define R_API // for static linking +# endif +# define R_API_CALLCONV __stdcall #else -#define R_API // for static linking -#endif -#define R_API_CALLCONV __stdcall -#else -#if __GNUC__ >= 4 -#define R_API __attribute__((visibility("default"))) -#else -#define R_API -#endif -#define R_API_CALLCONV +# if __GNUC__ >= 4 +# define R_API __attribute__((visibility("default"))) +# else +# define R_API +# endif +# define R_API_CALLCONV #endif #include typedef enum { - DATA_DATA, /**< pointer to data is stored */ - DATA_INT, /**< pointer to integer is stored */ + DATA_DATA, /**< pointer to data is stored */ + DATA_INT, /**< pointer to integer is stored */ DATA_DOUBLE, /**< pointer to a double is stored */ DATA_STRING, /**< pointer to a string is stored */ - DATA_ARRAY, /**< pointer to an array of values is stored */ - DATA_COUNT, /**< invalid */ + DATA_ARRAY, /**< pointer to an array of values is stored */ + DATA_COUNT, /**< invalid */ DATA_FORMAT, /**< indicates the following value is formatted */ - DATA_COND, /**< add data only if condition is true, skip otherwise */ + DATA_COND, /**< add data only if condition is true, skip otherwise */ } data_type_t; typedef struct data_array { int num_values; data_type_t type; - void *values; + void* values; } data_array_t; // Note: Do not unwrap a packed array to data_value_t, // on 32-bit the union has different size/alignment than a pointer. typedef union data_value { - int v_int; /**< A data value of type int, 4 bytes size/alignment */ + int v_int; /**< A data value of type int, 4 bytes size/alignment */ double v_dbl; /**< A data value of type double, 8 bytes size/alignment */ - void *v_ptr; /**< A data value pointer, 4/8 bytes size/alignment */ + void* v_ptr; /**< A data value pointer, 4/8 bytes size/alignment */ } data_value_t; typedef struct data { - struct data *next; /**< chaining to the next element in the linked list; NULL - indicates end-of-list */ - char *key; - char *pretty_key; /**< the name used for displaying data to user in with a - nicer name */ - char *format; /**< if not null, contains special formatting string */ + struct data* next; /**< chaining to the next element in the linked list; NULL indicates end-of-list */ + char* key; + char* pretty_key; /**< the name used for displaying data to user in with a nicer name */ + char* format; /**< if not null, contains special formatting string */ data_value_t value; data_type_t type; - unsigned retain; /**< incremented on data_retain, data_free only frees if this - is zero */ + unsigned retain; /**< incremented on data_retain, data_free only frees if this is zero */ } data_t; /** Constructs a structured data object. @@ -93,10 +87,10 @@ typedef struct data { Example: data_make( "key", "Pretty key", DATA_INT, 42, - "others", "More data", DATA_DATA, data_make("foo", - DATA_DOUBLE, 42.0, NULL), "zoom", NULL, data_array(2, - DATA_STRING, (char*[]){"hello", "World"}), "double", "Double", - DATA_DOUBLE, 10.0/3, NULL); + "others", "More data", DATA_DATA, data_make("foo", DATA_DOUBLE, 42.0, NULL), + "zoom", NULL, data_array(2, DATA_STRING, (char*[]){"hello", "World"}), + "double", "Double", DATA_DOUBLE, 10.0/3, + NULL); Most of the time the function copies perhaps what you expect it to. Things it copies: @@ -112,29 +106,26 @@ typedef struct data { copied deeply. Otherwise, it is copied shallowly. @param key Name of the first value to put in. - @param pretty_key Pretty name for the key. Use "" if to omit pretty label - for this field completely, or NULL if to use key name for it. - @param ... Type and then value of the item to put in, followed by the rest - of the key-type-values. The list is terminated with a NULL. + @param pretty_key Pretty name for the key. Use "" if to omit pretty label for this field completely, + or NULL if to use key name for it. + @param ... Type and then value of the item to put in, followed by the rest of the + key-type-values. The list is terminated with a NULL. - @return A constructed data_t* object or NULL if there was a memory - allocation error. + @return A constructed data_t* object or NULL if there was a memory allocation error. */ -R_API data_t *data_make(const char *key, const char *pretty_key, ...); +R_API data_t* data_make(const char* key, const char* pretty_key, ...); /** Adds to a structured data object, by appending data. @see data_make() */ -R_API data_t *data_append(data_t *first, const char *key, - const char *pretty_key, ...); +R_API data_t* data_append(data_t* first, const char* key, const char* pretty_key, ...); /** Adds to a structured data object, by prepending data. @see data_make() */ -R_API data_t *data_prepend(data_t *first, const char *key, - const char *pretty_key, ...); +R_API data_t* data_prepend(data_t* first, const char* key, const char* pretty_key, ...); /** Constructs an array from given data of the given uniform type. @@ -142,67 +133,54 @@ R_API data_t *data_prepend(data_t *first, const char *key, @param type The type of values to be copied. @param ptr The contents pointed by the argument are copied in. - @return The constructed data array object, typically placed inside a data_t - or NULL if there was a memory allocation error. + @return The constructed data array object, typically placed inside a data_t or NULL + if there was a memory allocation error. */ -R_API data_array_t *data_array(int num_values, data_type_t type, - void const *ptr); +R_API data_array_t* data_array(int num_values, data_type_t type, void const* ptr); /** Releases a data array. */ -R_API void data_array_free(data_array_t *array); +R_API void data_array_free(data_array_t* array); /** Retain a structure object, returns the structure object passed in. */ -R_API data_t *data_retain(data_t *data); +R_API data_t* data_retain(data_t* data); -/** Releases a structure object if retain is zero, decrement retain otherwise. - */ -R_API void data_free(data_t *data); +/** Releases a structure object if retain is zero, decrement retain otherwise. */ +R_API void data_free(data_t* data); struct data_output; typedef struct data_output { - void(R_API_CALLCONV *print_data)(struct data_output *output, data_t *data, - char const *format); - void(R_API_CALLCONV *print_array)(struct data_output *output, - data_array_t *data, char const *format); - void(R_API_CALLCONV *print_string)(struct data_output *output, - const char *data, char const *format); - void(R_API_CALLCONV *print_double)(struct data_output *output, double data, - char const *format); - void(R_API_CALLCONV *print_int)(struct data_output *output, int data, - char const *format); - void(R_API_CALLCONV *output_start)(struct data_output *output, - char const *const *fields, int num_fields); - void(R_API_CALLCONV *output_print)(struct data_output *output, data_t *data); - void(R_API_CALLCONV *output_free)(struct data_output *output); - int log_level; ///< the maximum log level (verbosity) allowed, more verbose - ///< messages must be ignored. + void(R_API_CALLCONV* print_data)(struct data_output* output, data_t* data, char const* format); + void(R_API_CALLCONV* print_array)(struct data_output* output, data_array_t* data, char const* format); + void(R_API_CALLCONV* print_string)(struct data_output* output, const char* data, char const* format); + void(R_API_CALLCONV* print_double)(struct data_output* output, double data, char const* format); + void(R_API_CALLCONV* print_int)(struct data_output* output, int data, char const* format); + void(R_API_CALLCONV* output_start)(struct data_output* output, char const* const* fields, int num_fields); + void(R_API_CALLCONV* output_print)(struct data_output* output, data_t* data); + void(R_API_CALLCONV* output_free)(struct data_output* output); + int log_level; ///< the maximum log level (verbosity) allowed, more verbose messages must be ignored. } data_output_t; /** Setup known field keys and start output, used by CSV only. @param output the data_output handle from data_output_x_create - @param fields the list of fields to accept and expect. Array is copied, but - the actual strings not. The list may contain duplicates and they are - eliminated. + @param fields the list of fields to accept and expect. Array is copied, but the actual + strings not. The list may contain duplicates and they are eliminated. @param num_fields number of fields */ -R_API void data_output_start(struct data_output *output, - char const *const *fields, int num_fields); +R_API void data_output_start(struct data_output* output, char const* const* fields, int num_fields); /** Prints a structured data object, flushes the output if applicable. */ -R_API void data_output_print(struct data_output *output, data_t *data); +R_API void data_output_print(struct data_output* output, data_t* data); -R_API void data_output_free(struct data_output *output); +R_API void data_output_free(struct data_output* output); /* data output helpers */ -R_API void print_value(data_output_t *output, data_type_t type, - data_value_t value, char const *format); +R_API void print_value(data_output_t* output, data_type_t type, data_value_t value, char const* format); -R_API void print_array_value(data_output_t *output, data_array_t *array, - char const *format, int idx); +R_API void print_array_value(data_output_t* output, data_array_t* array, char const* format, int idx); -R_API size_t data_print_jsons(data_t *data, char *dst, size_t len); +R_API size_t data_print_jsons(data_t* data, char* dst, size_t len); #endif // INCLUDE_DATA_H_ diff --git a/include/pulse_analyzer.h b/include/pulse_analyzer.h index 5093e79c..6935f7e4 100644 --- a/include/pulse_analyzer.h +++ b/include/pulse_analyzer.h @@ -14,7 +14,9 @@ #include "pulse_detect.h" +struct r_device; + /// Analyze and print result. -void pulse_analyzer(pulse_data_t *data, int package_type); +void pulse_analyzer(pulse_data_t *data, int package_type, struct r_device *device); #endif /* INCLUDE_PULSE_ANALYZER_H_ */ diff --git a/include/pulse_data.h b/include/pulse_data.h index 62579125..17776070 100644 --- a/include/pulse_data.h +++ b/include/pulse_data.h @@ -13,43 +13,33 @@ #ifndef INCLUDE_PULSE_DATA_H_ #define INCLUDE_PULSE_DATA_H_ -#include "data.h" #include #include -#define PD_MAX_PULSES \ - 1200 // Maximum number of pulses before forcing End Of Package -#define PD_MIN_PULSES \ - 16 // Minimum number of pulses before declaring a proper package -#define PD_MIN_PULSE_SAMPLES \ - 10 // Minimum number of samples in a pulse for proper detection -#define PD_MIN_GAP_MS \ - 10 // Minimum gap size in milliseconds to exceed to declare End Of Package -#define PD_MAX_GAP_MS \ - 100 // Maximum gap size in milliseconds to exceed to declare End Of Package -#define PD_MAX_GAP_RATIO \ - 10 // Ratio gap/pulse width to exceed to declare End Of Package (heuristic) -#define PD_MAX_PULSE_MS \ - 100 // Pulse width in ms to exceed to declare End Of Package (e.g. for non OOK - // packages) +#include "data.h" + +#define PD_MAX_PULSES 1200 // Maximum number of pulses before forcing End Of Package +#define PD_MIN_PULSES 16 // Minimum number of pulses before declaring a proper package +#define PD_MIN_PULSE_SAMPLES 10 // Minimum number of samples in a pulse for proper detection +#define PD_MIN_GAP_MS 10 // Minimum gap size in milliseconds to exceed to declare End Of Package +#define PD_MAX_GAP_MS 100 // Maximum gap size in milliseconds to exceed to declare End Of Package +#define PD_MAX_GAP_RATIO 10 // Ratio gap/pulse width to exceed to declare End Of Package (heuristic) +#define PD_MAX_PULSE_MS 100 // Pulse width in ms to exceed to declare End Of Package (e.g. for non OOK packages) /// Data for a compact representation of generic pulse train. typedef struct pulse_data { - uint64_t offset; ///< Offset to first pulse in number of samples from start of - ///< stream. + uint64_t offset; ///< Offset to first pulse in number of samples from start of stream. uint32_t sample_rate; ///< Sample rate the pulses are recorded with. - unsigned depth_bits; ///< Sample depth in bits. - unsigned start_ago; ///< Start of first pulse in number of samples ago. - unsigned end_ago; ///< End of last pulse in number of samples ago. + unsigned depth_bits; ///< Sample depth in bits. + unsigned start_ago; ///< Start of first pulse in number of samples ago. + unsigned end_ago; ///< End of last pulse in number of samples ago. unsigned int num_pulses; int pulse[PD_MAX_PULSES]; ///< Width of pulses (high) in number of samples. - int gap[PD_MAX_PULSES]; ///< Width of gaps between pulses (low) in number of - ///< samples. - int ook_low_estimate; ///< Estimate for the OOK low level (base noise level) - ///< at beginning of package. + int gap[PD_MAX_PULSES]; ///< Width of gaps between pulses (low) in number of samples. + int ook_low_estimate; ///< Estimate for the OOK low level (base noise level) at beginning of package. int ook_high_estimate; ///< Estimate for the OOK high level at end of package. - int fsk_f1_est; ///< Estimate for the F1 frequency for FSK. - int fsk_f2_est; ///< Estimate for the F2 frequency for FSK. + int fsk_f1_est; ///< Estimate for the F1 frequency for FSK. + int fsk_f2_est; ///< Estimate for the F2 frequency for FSK. float freq1_hz; float freq2_hz; float centerfreq_hz; @@ -69,34 +59,33 @@ typedef struct pulse_data { } pulse_data_t; /// Clear the content of a pulse_data_t structure. -void pulse_data_clear(pulse_data_t *data); +void pulse_data_clear(pulse_data_t* data); /// Shift out part of the data to make room for more. -void pulse_data_shift(pulse_data_t *data); +void pulse_data_shift(pulse_data_t* data); /// Print the content of a pulse_data_t structure (for debug). -void pulse_data_print(pulse_data_t const *data); +void pulse_data_print(pulse_data_t const* data); /// Dump the content of a pulse_data_t structure as raw binary. -void pulse_data_dump_raw(uint8_t *buf, unsigned len, uint64_t buf_offset, - pulse_data_t const *data, uint8_t bits); +void pulse_data_dump_raw(uint8_t* buf, unsigned len, uint64_t buf_offset, pulse_data_t const* data, uint8_t bits); /// Print a header for the VCD format. -void pulse_data_print_vcd_header(FILE *file, uint32_t sample_rate); +void pulse_data_print_vcd_header(FILE* file, uint32_t sample_rate); /// Print the content of a pulse_data_t structure in VCD format. -void pulse_data_print_vcd(FILE *file, pulse_data_t const *data, int ch_id); +void pulse_data_print_vcd(FILE* file, pulse_data_t const* data, int ch_id); /// Read the next pulse_data_t structure from OOK text. -void pulse_data_load(FILE *file, pulse_data_t *data, uint32_t sample_rate); +void pulse_data_load(FILE* file, pulse_data_t* data, uint32_t sample_rate); /// Print a header for the OOK text format. -void pulse_data_print_pulse_header(FILE *file); +void pulse_data_print_pulse_header(FILE* file); /// Print the content of a pulse_data_t structure as OOK text. -void pulse_data_dump(FILE *file, pulse_data_t const *data); +void pulse_data_dump(FILE* file, pulse_data_t const* data); /// Print the content of a pulse_data_t structure as OOK json. -data_t *pulse_data_print_data(pulse_data_t const *data); +data_t* pulse_data_print_data(pulse_data_t const* data); #endif /* INCLUDE_PULSE_DATA_H_ */ diff --git a/include/r_private.h b/include/r_private.h index 3b8bed36..2dc1a20c 100644 --- a/include/r_private.h +++ b/include/r_private.h @@ -49,7 +49,7 @@ struct dm_state { int analyze_pulses; file_info_t load_info; list_t dumper; - */ + /* Protocol states */ list_t r_devs; diff --git a/include/rtl_433.h b/include/rtl_433.h index a2160314..2bdca5bd 100644 --- a/include/rtl_433.h +++ b/include/rtl_433.h @@ -5,26 +5,25 @@ #ifndef INCLUDE_RTL_433_H_ #define INCLUDE_RTL_433_H_ -#include "list.h" #include #include #include -#define DEFAULT_SAMPLE_RATE 250000 -#define DEFAULT_FREQUENCY 433920000 -#define DEFAULT_HOP_TIME (60 * 10) -#define DEFAULT_ASYNC_BUF_NUMBER \ - 0 // Force use of default value (librtlsdr default: 15) -#define DEFAULT_BUF_LENGTH (16 * 32 * 512) // librtlsdr default +#include "list.h" + +#define DEFAULT_SAMPLE_RATE 250000 +#define DEFAULT_FREQUENCY 433920000 +#define DEFAULT_HOP_TIME (60 * 10) +#define DEFAULT_ASYNC_BUF_NUMBER 0 // Force use of default value (librtlsdr default: 15) +#define DEFAULT_BUF_LENGTH (16 * 32 * 512) // librtlsdr default #define FSK_PULSE_DETECTOR_LIMIT 800000000 -#define MINIMAL_BUF_LENGTH 512 -#define MAXIMAL_BUF_LENGTH (256 * 16384) +#define MINIMAL_BUF_LENGTH 512 +#define MAXIMAL_BUF_LENGTH (256 * 16384) #define SIGNAL_GRABBER_BUFFER (12 * DEFAULT_BUF_LENGTH) -#define MAX_FREQS 32 +#define MAX_FREQS 32 -#define INPUT_LINE_MAX \ - 8192 /**< enough for a complete textual bitbuffer (25*256) */ +#define INPUT_LINE_MAX 8192 /**< enough for a complete textual bitbuffer (25*256) */ struct sdr_dev; struct r_device; @@ -61,89 +60,84 @@ typedef enum { typedef struct r_cfg { /* - device_mode_t dev_mode; ///< Input device run mode - device_state_t dev_state; ///< Input device run state - char *dev_query; - char const *dev_info; - char *gain_str; - char *settings_str; - int ppm_error; - uint32_t out_block_size; - char const *test_data; - list_t in_files; - char const *in_filename; - int in_replay; - volatile sig_atomic_t hop_now; - volatile sig_atomic_t exit_async; - volatile sig_atomic_t exit_code; ///< 0=no err, 1=params or cmd line err, - 2=sdr device read error, 3=usb init error, 5=USB error (reset), other=other - error int frequencies; int frequency_index; uint32_t frequency[MAX_FREQS]; - uint32_t center_frequency; - int fsk_pulse_detect_mode; - int hop_times; - int hop_time[MAX_FREQS]; - time_t hop_start_time; - int duration; - time_t stop_time; - int after_successful_events_flag; - uint32_t samp_rate; - uint64_t input_pos; - uint32_t bytes_to_read; - struct sdr_dev *dev; - int grab_mode; ///< Signal grabber mode: 0=off, 1=all, 2=unknown, 3=known - int raw_mode; ///< Raw pulses printing mode: 0=off, 1=all, 2=unknown, 3=known - */ - int verbosity; ///< 0=normal, 1=verbose, 2=verbose decoders, 3=debug decoders, - ///< 4=trace decoding. - // int verbose_bits; + device_mode_t dev_mode; ///< Input device run mode + device_state_t dev_state; ///< Input device run state + char *dev_query; + char const *dev_info; + char *gain_str; + char *settings_str; + int ppm_error; + uint32_t out_block_size; + char const *test_data; + list_t in_files; + char const *in_filename; + int in_replay; + volatile sig_atomic_t hop_now; + volatile sig_atomic_t exit_async; + volatile sig_atomic_t exit_code; ///< 0=no err, 1=params or cmd line err, 2=sdr device read error, 3=usb init error, 5=USB error (reset), other=other error + int frequencies; + int frequency_index; + uint32_t frequency[MAX_FREQS]; + uint32_t center_frequency; + int fsk_pulse_detect_mode; + int hop_times; + int hop_time[MAX_FREQS]; + time_t hop_start_time; + int duration; + time_t stop_time; + int after_successful_events_flag; + uint32_t samp_rate; + uint64_t input_pos; + uint32_t bytes_to_read; + struct sdr_dev *dev; + int grab_mode; ///< Signal grabber mode: 0=off, 1=all, 2=unknown, 3=known + int raw_mode; ///< Raw pulses printing mode: 0=off, 1=all, 2=unknown, 3=known + */ + int verbosity; ///< 0=normal, 1=verbose, 2=verbose decoders, 3=debug decoders, 4=trace decoding. + int verbose_bits; conversion_mode_t conversion_mode; /* - int report_meta; - int report_noise; - int report_protocol; - time_mode_t report_time; - int report_time_hires; - int report_time_tz; - int report_time_utc; - int report_description; - int report_stats; - int stats_interval; - volatile sig_atomic_t stats_now; - time_t stats_time; - int no_default_devices; - */ - struct r_device *devices; + int report_meta; + int report_noise; + int report_protocol; + time_mode_t report_time; + int report_time_hires; + int report_time_tz; + int report_time_utc; + int report_description; + int report_stats; + int stats_interval; + volatile sig_atomic_t stats_now; + time_t stats_time; + int no_default_devices; + */ + struct r_device* devices; uint16_t num_r_devices; - - // list_t data_tags; + // list_t data_tags; list_t output_handler; - /* - list_t raw_handler; - */ - // int has_logout; - - struct dm_state *demod; - // char const *sr_filename; - - // int sr_execopen; - // int watchdog; ///< SDR acquire stall watchdog + // list_t raw_handler; + // int has_logout; + struct dm_state* demod; + // char const *sr_filename; + // int sr_execopen; + // int watchdog; ///< SDR acquire stall watchdog /* stats*/ - // time_t frames_since; ///< stats start time - // unsigned frames_count; ///< stats counter for interval - // unsigned frames_fsk; ///< stats counter for interval - // unsigned frames_events; ///< stats counter for interval - // struct mg_mgr *mgr; + // time_t frames_since; ///< stats start time + // unsigned frames_count; ///< stats counter for interval + // unsigned frames_fsk; ///< stats counter for interval + // unsigned frames_events; ///< stats counter for interval + // struct mg_mgr* mgr; // // TODO: rtl_433_ESP additions // - char *messageBuffer; // message buffer for message callback - int bufferSize; // size of message buffer for message callback + char* messageBuffer; // message buffer for message callback + int bufferSize; // size of message buffer for message callback /** * callback to controlling program to be executed when a message is received. * Object point passed is a pointer to a JSON formatted message for * publishing. */ - void (*callback)(char *message); + void (*callback)(char* message); } r_cfg_t; #endif /* INCLUDE_RTL_433_H_ */ diff --git a/include/rtl_433_devices.h b/include/rtl_433_devices.h index 03b5f3f1..f1500d7c 100644 --- a/include/rtl_433_devices.h +++ b/include/rtl_433_devices.h @@ -8,257 +8,267 @@ #include "r_device.h" #ifndef MY_DEVICES -# define DEVICES \ - DECL(abmt) \ - DECL(acurite_rain_896) \ - DECL(acurite_th) \ - DECL(acurite_txr) \ - DECL(acurite_986) \ - DECL(acurite_606) \ - DECL(acurite_00275rm) \ - DECL(acurite_590tx) \ - DECL(acurite_01185m) \ - DECL(akhan_100F14) \ - DECL(alectov1) \ - DECL(ambient_weather) \ - DECL(ambientweather_tx8300) \ - DECL(ambientweather_wh31e) \ - DECL(ant_antplus) \ - DECL(archos_tbh) \ - DECL(atech_ws308) \ - DECL(auriol_4ld5661) \ - DECL(auriol_aft77b2) \ - DECL(auriol_afw2a1) \ - DECL(auriol_ahfl) \ - DECL(auriol_hg02832) \ - DECL(badger_orion) \ - DECL(baldr_rain) \ - DECL(blyss) \ - DECL(brennenstuhl_rcs_2044) \ - DECL(bresser_3ch) \ - DECL(bresser_5in1) \ - DECL(bresser_6in1) \ - DECL(bresser_7in1) \ - DECL(bt_rain) \ - DECL(burnhardbbq) \ - DECL(calibeur_RF104) \ - DECL(cardin) \ - DECL(cavius) \ - DECL(ced7000) \ - DECL(celsia_czc1) \ - DECL(chuango) \ - DECL(cmr113) \ - DECL(companion_wtr001) \ - DECL(cotech_36_7959) \ - DECL(current_cost) \ - DECL(danfoss_CFR) \ - DECL(digitech_xc0324) \ - DECL(directv) \ - DECL(dish_remote_6_3) \ - DECL(dsc_security) \ - DECL(dsc_security_ws4945) \ - DECL(ecodhome) \ - DECL(ecowitt) \ - DECL(efergy_e2_classic) \ - DECL(efergy_optical) \ - DECL(eurochron_efth800) \ - DECL(elro_db286a) \ - DECL(elv_em1000) \ - DECL(elv_ws2000) \ - DECL(emax) \ - DECL(emontx) \ - DECL(emos_e6016) \ - DECL(emos_e6016_rain) \ - DECL(enocean_erp1) \ - DECL(ert_idm) \ - DECL(ert_netidm) \ - DECL(ert_scm) \ - DECL(esa_energy) \ - DECL(esic_emt7110) \ - DECL(esperanza_ews) \ - DECL(eurochron) \ - DECL(fineoffset_WH2) \ - DECL(fineoffset_WH25) \ - DECL(fineoffset_WH51) \ - DECL(fineoffset_WH0530) \ - DECL(fineoffset_wh1050) \ - DECL(fineoffset_wh1080) \ - DECL(fineoffset_wh1080_fsk) \ - DECL(fineoffset_wh31l) \ - DECL(fineoffset_wh45) \ - DECL(fineoffset_wn34) \ - DECL(fineoffset_ws80) \ - DECL(flowis) \ - DECL(fordremote) \ - DECL(fs20) \ - DECL(ft004b) \ - DECL(funkbus_remote) \ - DECL(gasmate_ba1008) \ - DECL(ge_coloreffects) \ - DECL(generic_motion) \ - DECL(generic_remote) \ - DECL(generic_temperature_sensor) \ - DECL(geo_minim) \ - DECL(govee) \ - DECL(govee_h5054) \ - DECL(gt_tmbbq05) \ - DECL(gt_wt_02) \ - DECL(gt_wt_03) \ - DECL(hcs200) \ - DECL(hcs200_fsk) \ - DECL(hideki_ts04) \ - DECL(holman_ws5029pcm) \ - DECL(holman_ws5029pwm) \ - DECL(hondaremote) \ - DECL(honeywell) \ - DECL(honeywell_cm921) \ - DECL(honeywell_wdb) \ - DECL(honeywell_wdb_fsk) \ - DECL(ht680) \ - DECL(ibis_beacon) \ - DECL(ikea_sparsnas) \ - DECL(infactory) \ - DECL(inkbird_ith20r) \ - DECL(kw9015b) \ - DECL(insteon) \ - DECL(interlogix) \ - DECL(intertechno) \ - DECL(jasco) \ - DECL(kedsum) \ - DECL(kerui) \ - DECL(klimalogg) \ - DECL(lacrossetx) \ - DECL(lacrosse_breezepro) \ - DECL(lacrosse_r1) \ - DECL(lacrosse_th3) \ - DECL(lacrosse_tx141x) \ - DECL(lacrosse_tx31u) \ - DECL(lacrosse_tx34) \ - DECL(lacrosse_tx29) \ - DECL(lacrosse_tx35) \ - DECL(lacrosse_wr1) \ - DECL(lacrosse_ws7000) \ - DECL(lacrossews) \ - DECL(lightwave_rf) \ - DECL(m_bus_mode_c_t) \ - DECL(m_bus_mode_c_t_downlink) \ - DECL(m_bus_mode_s) \ - DECL(m_bus_mode_r) \ - DECL(m_bus_mode_f) \ - DECL(markisol) \ - DECL(marlec_solar) \ - DECL(maverick_et73) \ - DECL(maverick_et73x) \ - DECL(maverick_xr30) \ - DECL(mebus433) \ - DECL(megacode) \ - DECL(missil_ml0757) \ - DECL(neptune_r900) \ - DECL(new_template) \ - DECL(newkaku) \ - DECL(nexa) \ - DECL(nexus) \ - DECL(nice_flor_s) \ - DECL(norgo) \ - DECL(oil_smart) \ - DECL(oil_standard) \ - DECL(oil_standard_ask) \ - DECL(oil_watchman) \ - DECL(oil_watchman_advanced) \ - DECL(opus_xt300) \ - DECL(oregon_scientific) \ - DECL(oregon_scientific_sl109h) \ - DECL(oregon_scientific_v1) \ - DECL(philips_aj3650) \ - DECL(philips_aj7010) \ - DECL(proflame2) \ - DECL(prologue) \ - DECL(proove) \ - DECL(quhwa) \ - DECL(radiohead_ask) \ - DECL(sensible_living) \ - DECL(rainpoint) \ - DECL(regency_fan) \ - DECL(revolt_nc5462) \ - DECL(rftech) \ - DECL(rojaflex) \ - DECL(rubicson) \ - DECL(rubicson_48659) \ - DECL(rubicson_pool_48942) \ - DECL(s3318p) \ - DECL(schraeder) \ - DECL(schrader_EG53MA4) \ - DECL(schrader_SMD3MA4) \ - DECL(scmplus) \ - DECL(secplus_v1) \ - DECL(sharp_spc775) \ - DECL(silvercrest) \ - DECL(ss_sensor) \ - DECL(simplisafe_gen3) \ - DECL(skylink_motion) \ - DECL(smoke_gs558) \ - DECL(solight_te44) \ - DECL(somfy_iohc) \ - DECL(somfy_rts) \ - DECL(springfield) \ - DECL(srsmith_pool_srs_2c_tx) \ - DECL(steelmate) \ - DECL(telldus_ft0385r) \ - DECL(tfa_14_1504_v2) \ - DECL(tfa_303196) \ - DECL(tfa_30_3221) \ - DECL(tfa_drop_303233) \ - DECL(tfa_marbella) \ - DECL(tfa_pool_thermometer) \ - DECL(tfa_twin_plus_303049) \ - DECL(thermopro_tp11) \ - DECL(thermopro_tp12) \ - DECL(thermopro_tx2) \ - DECL(tpms_abarth124) \ - DECL(tpms_ave) \ - DECL(tpms_citroen) \ - DECL(tpms_eezrv) \ - DECL(tpms_elantra2012) \ - DECL(tpms_ford) \ - DECL(tpms_hyundai_vdo) \ - DECL(tpms_jansite) \ - DECL(tpms_jansite_solar) \ - DECL(tpms_kia) \ - DECL(tpms_pmv107j) \ - DECL(tpms_porsche) \ - DECL(tpms_renault) \ - DECL(tpms_renault_0435r) \ - DECL(tpms_toyota) \ - DECL(tpms_truck) \ - DECL(tpms_tyreguard400) \ - DECL(ts_ft002) \ - DECL(ttx201) \ - DECL(vaillant_vrt340f) \ - DECL(vauno_en8822c) \ - DECL(visonic_powercode) \ - DECL(waveman) \ - DECL(wec2103) \ - DECL(wg_pb12v1) \ - DECL(ws2032) \ - DECL(wssensor) \ - DECL(wt1024) \ - DECL(wt450) \ - DECL(X10_RF) \ - DECL(x10_sec) \ - DECL(yale_hsa) \ - /* Add new decoders here. */ -# define NUMOF_OOK_DEVICES 157 -# define NUMOF_FSK_DEVICES 80 -/* Add new decoders here. */ +#define DEVICES \ + DECL(abmt) \ + DECL(acurite_rain_896) \ + DECL(acurite_th) \ + DECL(acurite_txr) \ + DECL(acurite_986) \ + DECL(acurite_606) \ + DECL(acurite_00275rm) \ + DECL(acurite_590tx) \ + DECL(acurite_01185m) \ + DECL(akhan_100F14) \ + DECL(alectov1) \ + DECL(ambient_weather) \ + DECL(ambientweather_tx8300) \ + DECL(ambientweather_wh31e) \ + DECL(ant_antplus) \ + DECL(archos_tbh) \ + DECL(atech_ws308) \ + DECL(auriol_4ld5661) \ + DECL(auriol_aft77b2) \ + DECL(auriol_afw2a1) \ + DECL(auriol_ahfl) \ + DECL(auriol_hg02832) \ + DECL(badger_orion) \ + DECL(baldr_rain) \ + DECL(blyss) \ + DECL(brennenstuhl_rcs_2044) \ + DECL(bresser_3ch) \ + DECL(bresser_5in1) \ + DECL(bresser_6in1) \ + DECL(bresser_7in1) \ + DECL(bresser_leakage) \ + DECL(bresser_lightning) \ + DECL(bt_rain) \ + DECL(burnhardbbq) \ + DECL(calibeur_RF104) \ + DECL(cardin) \ + DECL(cavius) \ + DECL(ced7000) \ + DECL(celsia_czc1) \ + DECL(chuango) \ + DECL(cmr113) \ + DECL(companion_wtr001) \ + DECL(cotech_36_7959) \ + DECL(current_cost) \ + DECL(danfoss_CFR) \ + DECL(digitech_xc0324) \ + DECL(directv) \ + DECL(dish_remote_6_3) \ + DECL(dsc_security) \ + DECL(dsc_security_ws4945) \ + DECL(ecodhome) \ + DECL(ecowitt) \ + DECL(efergy_e2_classic) \ + DECL(efergy_optical) \ + DECL(eurochron_efth800) \ + DECL(elro_db286a) \ + DECL(elv_em1000) \ + DECL(elv_ws2000) \ + DECL(emax) \ + DECL(emontx) \ + DECL(emos_e6016) \ + DECL(emos_e6016_rain) \ + DECL(enocean_erp1) \ + DECL(ert_idm) \ + DECL(ert_netidm) \ + DECL(ert_scm) \ + DECL(esa_energy) \ + DECL(esic_emt7110) \ + DECL(esperanza_ews) \ + DECL(eurochron) \ + DECL(fineoffset_WH2) \ + DECL(fineoffset_WH25) \ + DECL(fineoffset_WH51) \ + DECL(fineoffset_WH0530) \ + DECL(fineoffset_wh1050) \ + DECL(tfa_303151) \ + DECL(fineoffset_wh1080) \ + DECL(fineoffset_wh1080_fsk) \ + DECL(fineoffset_wh31l) \ + DECL(fineoffset_wh45) \ + DECL(fineoffset_wn34) \ + DECL(fineoffset_ws80) \ + DECL(fineoffset_ws90) \ + DECL(flowis) \ + DECL(fordremote) \ + DECL(fs20) \ + DECL(ft004b) \ + DECL(funkbus_remote) \ + DECL(gasmate_ba1008) \ + DECL(ge_coloreffects) \ + DECL(generic_motion) \ + DECL(generic_remote) \ + DECL(generic_temperature_sensor) \ + DECL(geo_minim) \ + DECL(govee) \ + DECL(govee_h5054) \ + DECL(gt_tmbbq05) \ + DECL(gt_wt_02) \ + DECL(gt_wt_03) \ + DECL(hcs200) \ + DECL(hcs200_fsk) \ + DECL(hideki_ts04) \ + DECL(holman_ws5029pcm) \ + DECL(holman_ws5029pwm) \ + DECL(hondaremote) \ + DECL(honeywell) \ + DECL(honeywell_cm921) \ + DECL(honeywell_wdb) \ + DECL(honeywell_wdb_fsk) \ + DECL(ht680) \ + DECL(ibis_beacon) \ + DECL(ikea_sparsnas) \ + DECL(infactory) \ + DECL(inkbird_ith20r) \ + DECL(kw9015b) \ + DECL(insteon) \ + DECL(interlogix) \ + DECL(intertechno) \ + DECL(jasco) \ + DECL(kedsum) \ + DECL(kerui) \ + DECL(klimalogg) \ + DECL(lacrossetx) \ + DECL(lacrosse_breezepro) \ + DECL(lacrosse_r1) \ + DECL(lacrosse_th3) \ + DECL(lacrosse_tx141x) \ + DECL(lacrosse_tx31u) \ + DECL(lacrosse_tx34) \ + DECL(lacrosse_tx29) \ + DECL(lacrosse_tx35) \ + DECL(lacrosse_wr1) \ + DECL(lacrosse_ws7000) \ + DECL(lacrossews) \ + DECL(lightwave_rf) \ + DECL(m_bus_mode_c_t) \ + DECL(m_bus_mode_c_t_downlink) \ + DECL(m_bus_mode_s) \ + DECL(m_bus_mode_r) \ + DECL(m_bus_mode_f) \ + DECL(markisol) \ + DECL(marlec_solar) \ + DECL(maverick_et73) \ + DECL(maverick_et73x) \ + DECL(maverick_xr30) \ + DECL(mebus433) \ + DECL(megacode) \ + DECL(missil_ml0757) \ + DECL(neptune_r900) \ + DECL(new_template) \ + DECL(newkaku) \ + DECL(nexa) \ + DECL(nexus) \ + DECL(nice_flor_s) \ + DECL(norgo) \ + DECL(oil_smart) \ + DECL(oil_standard) \ + DECL(oil_standard_ask) \ + DECL(oil_watchman) \ + DECL(oil_watchman_advanced) \ + DECL(opus_xt300) \ + DECL(oregon_scientific) \ + DECL(oregon_scientific_sl109h) \ + DECL(oregon_scientific_v1) \ + DECL(philips_aj3650) \ + DECL(philips_aj7010) \ + DECL(proflame2) \ + DECL(prologue) \ + DECL(proove) \ + DECL(quhwa) \ + DECL(radiohead_ask) \ + DECL(sensible_living) \ + DECL(rainpoint) \ + DECL(regency_fan) \ + DECL(revolt_nc5462) \ + DECL(rftech) \ + DECL(rojaflex) \ + DECL(rubicson) \ + DECL(rubicson_48659) \ + DECL(rubicson_pool_48942) \ + DECL(s3318p) \ + DECL(schou_72543_rain) \ + DECL(schraeder) \ + DECL(schrader_EG53MA4) \ + DECL(schrader_SMD3MA4) \ + DECL(scmplus) \ + DECL(secplus_v1) \ + DECL(sharp_spc775) \ + DECL(silvercrest) \ + DECL(ss_sensor) \ + DECL(simplisafe_gen3) \ + DECL(skylink_motion) \ + DECL(smoke_gs558) \ + DECL(solight_te44) \ + DECL(somfy_iohc) \ + DECL(somfy_rts) \ + DECL(springfield) \ + DECL(srsmith_pool_srs_2c_tx) \ + DECL(steelmate) \ + DECL(telldus_ft0385r) \ + DECL(tfa_14_1504_v2) \ + DECL(tfa_303196) \ + DECL(tfa_30_3221) \ + DECL(tfa_drop_303233) \ + DECL(tfa_marbella) \ + DECL(tfa_pool_thermometer) \ + DECL(tfa_twin_plus_303049) \ + DECL(thermopro_tp11) \ + DECL(thermopro_tp12) \ + DECL(thermopro_tx2) \ + DECL(thermopro_tx2c) \ + DECL(tpms_abarth124) \ + DECL(tpms_ave) \ + DECL(tpms_citroen) \ + DECL(tpms_eezrv) \ + DECL(tpms_elantra2012) \ + DECL(tpms_ford) \ + DECL(tpms_hyundai_vdo) \ + DECL(tpms_jansite) \ + DECL(tpms_jansite_solar) \ + DECL(tpms_kia) \ + DECL(tpms_nissan) \ + DECL(tpms_pmv107j) \ + DECL(tpms_porsche) \ + DECL(tpms_renault) \ + DECL(tpms_renault_0435r) \ + DECL(tpms_toyota) \ + DECL(tpms_truck) \ + DECL(tpms_tyreguard400) \ + DECL(ts_ft002) \ + DECL(ttx201) \ + DECL(vaillant_vrt340f) \ + DECL(vauno_en8822c) \ + DECL(visonic_powercode) \ + DECL(waveman) \ + DECL(wec2103) \ + DECL(wg_pb12v1) \ + DECL(ws2032) \ + DECL(wssensor) \ + DECL(wt1024) \ + DECL(wt450) \ + DECL(X10_RF) \ + DECL(x10_sec) \ + DECL(yale_hsa) \ + /* Add new decoders here. */ +#define NUMOF_OOK_DEVICES 159 +#define NUMOF_FSK_DEVICES 85 + /* Add new decoders here. */ #else /** * Subset of devices that I have access to and have tested with */ -# define DEVICES \ - DECL(lacrosse_tx141x) \ -/* Add new personal decoders here. */ -# define NUMOF_OOK_DEVICES 1 -# define NUMOF_FSK_DEVICES 0 +#define DEVICES \ + DECL(acurite_986) \ + DECL(skylink_motion) \ + DECL(prologue) \ + DECL(philips_aj3650) \ + DECL(fineoffset_WH51) \ + /* Add new personal decoders here. */ + #define NUMOFDEVICES 5 #endif #define DECL(name) extern r_device name; @@ -266,8 +276,8 @@ DEVICES #undef DECL #ifdef RTL_FLEX -extern r_device* flex_create_device(char* spec); // maybe put this in some header file? -# define NUMOFDEVICES 214 + extern r_device *flex_create_device(char *spec); // maybe put this in some header file? +#define NUMOFDEVICES 214 #endif #endif /* INCLUDE_RTL_433_DEVICES_H_ */ diff --git a/src/rtl_433/abuf.c b/src/rtl_433/abuf.c index da594882..8b3bc691 100644 --- a/src/rtl_433/abuf.c +++ b/src/rtl_433/abuf.c @@ -45,7 +45,7 @@ void abuf_cat(abuf_t *buf, char const *str) { size_t len = strlen(str); if (buf->left >= len + 1) { - strcpy(buf->tail, str); + memcpy(buf->tail, str, len + 1); buf->tail += len; buf->left -= len; } diff --git a/src/rtl_433/data.c b/src/rtl_433/data.c index c1335435..3a5ed257 100644 --- a/src/rtl_433/data.c +++ b/src/rtl_433/data.c @@ -151,6 +151,10 @@ R_API data_array_t *data_array(int num_values, data_type_t type, void const *val return NULL; } +// the static analyzer can't prove the allocs to be correct +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak" + static data_t *vdata_make(data_t *first, const char *key, const char *pretty_key, va_list ap) { data_type_t type; @@ -257,7 +261,6 @@ static data_t *vdata_make(data_t *first, const char *key, const char *pretty_key type = va_arg(ap, data_type_t); } } while (key); - va_end(ap); if (format) { fprintf(stderr, "vdata_make() format type without data\n"); goto alloc_error; @@ -344,6 +347,8 @@ R_API void data_free(data_t *data) } } +#pragma GCC diagnostic pop + /* data output */ R_API void data_output_print(data_output_t *output, data_t *data) diff --git a/src/rtl_433/decoder_util.c b/src/rtl_433/decoder_util.c index 6ea9e091..337fb337 100644 --- a/src/rtl_433/decoder_util.c +++ b/src/rtl_433/decoder_util.c @@ -56,6 +56,11 @@ static char *bitrow_asprint_code(uint8_t const *bitrow, unsigned bit_len) // remove last nibble if needed row_bytes[2 * (bit_len + 3) / 8] = '\0'; + // print at least one '0' + if (bit_len == 0) { + snprintf(row_bytes, sizeof(row_bytes), "0"); + } + // a simple bitrow representation row_code = malloc(8 + bit_len / 4 + 1); // "{nnnn}..\0" if (!row_code) { @@ -132,10 +137,11 @@ void decoder_log_bitbuffer(r_device *decoder, int level, char const *func, const // note that decoder levels start at LOG_WARNING level += 4; - char *row_codes[BITBUF_ROWS]; + char *row_codes[BITBUF_ROWS] = {0}; char *row_bits[BITBUF_ROWS] = {0}; - for (unsigned i = 0; i < bitbuffer->num_rows; i++) { + unsigned num_rows = bitbuffer->num_rows; + for (unsigned i = 0; i < num_rows; i++) { row_codes[i] = bitrow_asprint_code(bitbuffer->bb[i], bitbuffer->bits_per_row[i]); if (decoder->verbose_bits) { @@ -148,20 +154,20 @@ void decoder_log_bitbuffer(r_device *decoder, int level, char const *func, const "src", "", DATA_STRING, func, "lvl", "", DATA_INT, level, "msg", "", DATA_STRING, msg, - "num_rows", "", DATA_INT, bitbuffer->num_rows, - "codes", "", DATA_ARRAY, data_array(bitbuffer->num_rows, DATA_STRING, row_codes), + "num_rows", "", DATA_INT, num_rows, + "codes", "", DATA_ARRAY, data_array(num_rows, DATA_STRING, row_codes), NULL); /* clang-format on */ if (decoder->verbose_bits) { data_append(data, - "bits", "", DATA_ARRAY, data_array(bitbuffer->num_rows, DATA_STRING, row_bits), + "bits", "", DATA_ARRAY, data_array(num_rows, DATA_STRING, row_bits), NULL); } decoder_output_log(decoder, level, data); - for (unsigned i = 0; i < bitbuffer->num_rows; i++) { + for (unsigned i = 0; i < num_rows; i++) { free(row_codes[i]); free(row_bits[i]); } diff --git a/src/rtl_433/devices/acurite.c b/src/rtl_433/devices/acurite.c index f71c5128..cc3b3235 100644 --- a/src/rtl_433/devices/acurite.c +++ b/src/rtl_433/devices/acurite.c @@ -2,6 +2,7 @@ Acurite weather stations and temperature / humidity sensors. Copyright (c) 2015, Jens Jenson, Helge Weissig, David Ray Thompson, Robert Terzi + Enhanced Acurite-606TX by Boing 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 @@ -21,7 +22,7 @@ Devices decoded: - Acurite 609TXC "TH" temperature and humidity sensor (609A1TX) - Acurite 986 Refrigerator / Freezer Thermometer - Acurite 515 Refrigerator / Freezer Thermometer -- Acurite 606TX temperature sensor +- Acurite 606TX temperature sensor, optional with channels and [TX]Button - Acurite 6045M Lightning Detector - Acurite 00275rm and 00276rm temp. and humidity with optional probe. - Acurite 1190/1192 leak/water detector @@ -139,7 +140,7 @@ static char const *acurite_getChannel(uint8_t byte) } // Add exception and raw message bytes to message to enable -// later analysis of unexpected/possbily undecoded data +// later analysis of unexpected/possibly undecoded data static void data_append_exception(data_t* data, int exception, uint8_t* bb, int browlen) { char raw_str[31], *rawp; @@ -497,7 +498,7 @@ Acurite 899 Rain Gauge decoder static int acurite_899_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t *bb) { (void)bitbuffer; - // MIC (checkum, parity) validated in calling function + // MIC (checksum, parity) validated in calling function uint16_t sensor_id = ((bb[0] & 0x3f) << 8) | bb[1]; // int battery_low = (bb[2] & 0x40) == 0; @@ -505,7 +506,7 @@ static int acurite_899_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t /* @todo bug? channel output isn't consistent with the rest of he Acurite devices in this family, should output ('A', 'B', or 'C') - Currently outputing 00 = A, 01 = B, 10 = C + Currently outputting 00 = A, 01 = B, 10 = C Leaving as is to maintain compatibility for now */ @@ -547,7 +548,7 @@ Acurite 3n1 Weather Station decoder */ static int acurite_3n1_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t *bb) { - // MIC (checkum, parity) validated in calling function + // MIC (checksum, parity) validated in calling function (void)bitbuffer; char const* channel_str = acurite_getChannel(bb[0]); @@ -568,7 +569,7 @@ static int acurite_3n1_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t which was copied from 5n1, but existing code 3n1 uses 14 bits for ID. so these bits are used twice. - Leaving for compatibility, but probaby sequence_num + Leaving for compatibility, but probably sequence_num doesn't exist and should be deleted. If the 3n1 did use a sequence number, the ID would change on each output. */ @@ -631,7 +632,7 @@ XXX todo docs */ static int acurite_5n1_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t* bb) { - // MIC (checkum, parity) validated in calling function + // MIC (checksum, parity) validated in calling function (void)bitbuffer; char const* channel_str = acurite_getChannel(bb[0]); @@ -681,7 +682,7 @@ static int acurite_5n1_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t int temp_raw = (bb[4] & 0x0F) << 7 | (bb[5] & 0x7F); float tempf = (temp_raw - 400) * 0.1f; - if (tempf > 158.0) { + if (tempf < -40.0 || tempf > 158.0) { decoder_logf(decoder, 1, __func__, "5n1 0x%04X Ch %s, invalid temperature: %0.1f F", sensor_id, channel_str, tempf); return DECODE_FAIL_SANITY; @@ -832,7 +833,7 @@ static int acurite_atlas_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsig wind_speed_mph = ((bb[3] & 0x7F) << 1) | ((bb[4] & 0x40) >> 6); if (wind_speed_mph > 200) { - decoder_logf(decoder, 1, __func__, "Atlas 0x%04X Ch %s, invalid wind spped: %.1f MPH", + decoder_logf(decoder, 1, __func__, "Atlas 0x%04X Ch %s, invalid wind speed: %.1f MPH", sensor_id, channel_str, wind_speed_mph); return DECODE_FAIL_SANITY; } @@ -862,7 +863,7 @@ static int acurite_atlas_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsig exception++; tempf = (temp_raw - 400) * 0.1; - if (tempf > 158.0) { + if (tempf < -40.0 || tempf > 158.0) { decoder_logf(decoder, 1, __func__, "Atlas 0x%04X Ch %s, invalid temperature: %0.1f F", sensor_id, channel_str, tempf); return DECODE_FAIL_SANITY; @@ -1007,7 +1008,7 @@ Message Type 0x04, 7 bytes */ static int acurite_tower_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t *bb) { - // MIC (checkum, parity) validated in calling function + // MIC (checksum, parity) validated in calling function (void)bitbuffer; int exception = 0; @@ -1123,7 +1124,7 @@ CCII IIII | IIII IIII | pBMM MMMM | bTTT TTTT | bTTT TTTT | KKKK KKKK */ static int acurite_515_decode(r_device *decoder, bitbuffer_t *bitbuffer, uint8_t *bb) { - // length, MIC (checkum, parity) validated in calling function + // length, MIC (checksum, parity) validated in calling function (void)bitbuffer; int exception = 0; @@ -1654,6 +1655,8 @@ static int acurite_606_decode(r_device *decoder, bitbuffer_t *bitbuffer) int16_t temp_raw; // temperature as read from the data packet float temp_c; // temperature in C int battery_ok; // the battery status: 1 is good, 0 is low + int channel; // the channel + int button; // the reset button: 1: pressed int sensor_id; // the sensor ID - basically a random number that gets reset whenever the battery is removed row = bitbuffer_find_repeated_row(bitbuffer, 3, 32); // expected are 6 rows @@ -1682,6 +1685,8 @@ static int acurite_606_decode(r_device *decoder, bitbuffer_t *bitbuffer) // upper 4 bits of nibble 1 are reserved for other usages (e.g. battery status) sensor_id = b[0]; battery_ok = (b[1] & 0x80) >> 7; + channel = ((b[1] & 0x30) >> 4)+1; // Channel A,B,C / 1,2,3 + button = (b[1] & 0x40) >> 6; // SensorTX Button temp_raw = (int16_t)((b[1] << 12) | (b[2] << 4)); temp_raw = temp_raw >> 4; temp_c = temp_raw * 0.1f; @@ -1690,7 +1695,9 @@ static int acurite_606_decode(r_device *decoder, bitbuffer_t *bitbuffer) data = data_make( "model", "", DATA_STRING, "Acurite-606TX", "id", "", DATA_INT, sensor_id, + "channel", "Channel", DATA_INT, channel, "battery_ok", "Battery", DATA_INT, battery_ok, + "button", "Button" , DATA_INT, button, "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, "mic", "Integrity", DATA_STRING, "CHECKSUM", NULL); @@ -1767,8 +1774,8 @@ static int acurite_590tx_decode(r_device *decoder, bitbuffer_t *bitbuffer) data = data_make( "model", "", DATA_STRING, "Acurite-590TX", "id", "", DATA_INT, sensor_id, - "battery_ok", "Battery", DATA_INT, battery_ok, "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, battery_ok, "humidity", "Humidity", DATA_COND, humidity != -1, DATA_INT, humidity, "temperature_C", "Temperature", DATA_COND, humidity == -1, DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, "mic", "Integrity", DATA_STRING, "PARITY", @@ -2004,8 +2011,8 @@ static char const *const acurite_606_output_fields[] = { static char const *const acurite_590_output_fields[] = { "model", "id", - "battery_ok", "channel", + "battery_ok", "temperature_C", "humidity", "mic", @@ -2065,4 +2072,4 @@ r_device const acurite_590tx = { .sync_width = 500, // sync pulse is 632 us .decode_fn = &acurite_590tx_decode, .fields = acurite_590_output_fields, -}; \ No newline at end of file +}; diff --git a/src/rtl_433/devices/ambientweather_wh31e.c b/src/rtl_433/devices/ambientweather_wh31e.c index aca1d502..119cdb94 100644 --- a/src/rtl_433/devices/ambientweather_wh31e.c +++ b/src/rtl_433/devices/ambientweather_wh31e.c @@ -115,11 +115,12 @@ Seems to be the same as Fine Offset WH5360 or Ecowitt WH5360B. Data layout: - YY 00 IIII FF RRRR XX AA 00 02 ?? 00 00 + YY 00 IIII FV RRRR XX AA 00 02 ?? 00 00 - Y is a fixed Type Code of 0x40 - I is a device ID - F is perhaps flags, but only seen fixed 0x10 so far +- V is battery voltage, ( FV & 0x1f ) * 0.1f - R is the rain bucket tip count, 0.1mm increments - X is CRC-8, poly 0x31, init 0x00 - A is SUM-8 @@ -215,7 +216,7 @@ static int ambientweather_whx_decode(r_device *decoder, bitbuffer_t *bitbuffer) float temp_c = (temp_raw - 400) * 0.1f; int humidity = b[4]; char extra[11]; - sprintf(extra, "%02x%02x%02x%02x%02x", b[6], b[7], b[8], b[9], b[10]); + snprintf(extra, sizeof(extra), "%02x%02x%02x%02x%02x", b[6], b[7], b[8], b[9], b[10]); /* clang-format off */ data_t *data = data_make( @@ -257,7 +258,7 @@ static int ambientweather_whx_decode(r_device *decoder, bitbuffer_t *bitbuffer) int seconds = ((b[8] & 0x70) >> 4) * 10 + (b[8] & 0x0F); char clock_str[23]; - sprintf(clock_str, "%04d-%02d-%02dT%02d:%02d:%02dZ", + snprintf(clock_str, sizeof(clock_str), "%04d-%02d-%02dT%02d:%02d:%02dZ", year, month, day, hours, minutes, seconds); /* clang-format off */ @@ -287,23 +288,27 @@ static int ambientweather_whx_decode(r_device *decoder, bitbuffer_t *bitbuffer) } int id = (b[2] << 8) | b[3]; - //int battery_ok = (b[4] >> 7); - //int channel = ((b[4] & 0x70) >> 4) + 1; + int battery_v = (b[4] & 0x1f); + int battery_lvl = battery_v <= 9 ? 0 : ((battery_v - 9) / 6 * 100); // 0.9V-1.5V is 0-100 int rain_raw = (b[5] << 8) | b[6]; char extra[11]; - sprintf(extra, "%02x%02x%02x%02x%02x", b[9], b[10], b[11], b[12], b[13]); + snprintf(extra, sizeof(extra), "%02x%02x%02x%02x%02x", b[9], b[10], b[11], b[12], b[13]); + + if (battery_lvl > 100) + battery_lvl = 100; /* clang-format off */ data_t *data = data_make( - "model", "", DATA_STRING, "EcoWitt-WH40", - "id" , "", DATA_INT, id, - //"channel", "Channel", DATA_INT, channel, - //"battery_ok", "Battery", DATA_INT, battery_ok, - "rain_mm", "Total Rain", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_raw * 0.1, - "data", "Extra Data", DATA_STRING, extra, - "mic", "Integrity", DATA_STRING, "CRC", + "model", "", DATA_STRING, "EcoWitt-WH40", + "id" , "", DATA_INT, id, + "battery_V", "Battery Voltage", DATA_COND, battery_v != 0, DATA_FORMAT, "%f V", DATA_DOUBLE, battery_v * 0.1f, + "battery_ok", "Battery", DATA_COND, battery_v != 0, DATA_DOUBLE, battery_lvl * 0.01f, + "rain_mm", "Total Rain", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_raw * 0.1, + "data", "Extra Data", DATA_STRING, extra, + "mic", "Integrity", DATA_STRING, "CRC", NULL); /* clang-format on */ + decoder_output_data(decoder, data); events++; } @@ -329,7 +334,7 @@ static int ambientweather_whx_decode(r_device *decoder, bitbuffer_t *bitbuffer) int wgust = b[12]; int wdir = ((b[7] & 0x20) >> 5) | b[11]; char extra[7]; - sprintf(extra, "%02x %02x%01x", b[13], b[16], b[17] >> 4); + snprintf(extra, sizeof(extra), "%02x %02x%01x", b[13], b[16], b[17] >> 4); /* clang-format off */ data_t *data = data_make( @@ -361,6 +366,7 @@ static char const *const output_fields[] = { "id", "channel", "battery_ok", + "battery_v", "temperature_C", "humidity", "rain_mm", diff --git a/src/rtl_433/devices/ant_antplus.c b/src/rtl_433/devices/ant_antplus.c index a4e65bd7..ca4fcc7b 100644 --- a/src/rtl_433/devices/ant_antplus.c +++ b/src/rtl_433/devices/ant_antplus.c @@ -82,7 +82,6 @@ static int ant_antplus_decode(r_device *decoder, bitbuffer_t *bitbuffer) uint8_t const preamble[] = {0xAA}; uint8_t b[17]; // aligned packet data for both preambles/offsets unsigned bit_offset; - char payload[8 * 3 + 1]; // payload is 8 hex pairs for ANT and ANT+ int antplus_flag = 0; // validate buffer: ANT messages are shorter than 150us, i.e. ~140 bits at 1Mbps @@ -96,7 +95,7 @@ static int ant_antplus_decode(r_device *decoder, bitbuffer_t *bitbuffer) return DECODE_ABORT_LENGTH; } - // ANT and ANT+ packes have either aa or 55 preamble, depending on the first bit of + // ANT and ANT+ packets have either aa or 55 preamble, depending on the first bit of // the following byte. i.e. 10101010 1xxxxxxx or 01010101 0xxxxxxx // the best way to know which is being used, is to verify which one has a valid CRC // the following code relies on the fact that 55 is aa shifted right @@ -115,7 +114,8 @@ static int ant_antplus_decode(r_device *decoder, bitbuffer_t *bitbuffer) uint8_t device_type = b[4]; uint8_t tx_type = b[5]; // display ANT and ANT+ payload in the same format used by ANT tools - sprintf(payload, "%02x %02x %02x %02x %02x %02x %02x %02x", b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14]); + char payload[8 * 3 + 1]; // payload is 8 hex pairs for ANT and ANT+ + snprintf(payload, sizeof(payload), "%02x %02x %02x %02x %02x %02x %02x %02x", b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14]); // display ANT or ANT+ depending on the network key used. if (0xc5a6 == net_key) diff --git a/src/rtl_433/devices/archos_tbh.c b/src/rtl_433/devices/archos_tbh.c index a5f94d84..1f4812cb 100644 --- a/src/rtl_433/devices/archos_tbh.c +++ b/src/rtl_433/devices/archos_tbh.c @@ -51,7 +51,7 @@ Raw data frame (power index): - Index {24} - Timestamp {34} - MaxPower {16} -- some additinal data ??? +- some additional data ??? - CRC8 poly=0x7 the crc includes a length byte at the beginning */ diff --git a/src/rtl_433/devices/auriol_4ld5661.c b/src/rtl_433/devices/auriol_4ld5661.c index 3722e144..1a9aa3be 100644 --- a/src/rtl_433/devices/auriol_4ld5661.c +++ b/src/rtl_433/devices/auriol_4ld5661.c @@ -1,7 +1,8 @@ /** @file - Auriol 4-LD5661 sensor. + Auriol 4-LD5661/4-LD5972/4-LD6313 sensors. Copyright (C) 2021 Balazs H. + Copyright (C) 2023 Peter Soos 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 @@ -10,19 +11,19 @@ */ /** -Lidl Auriol 4-LD5661 sensor. +Lidl Auriol 4-LD5661/4-LD5972/4-LD6313 sensors. -See also issue #1857. +See also issues #1857, #2631 and PR #2633 Data layout: II B TTT F RRRRRR -- I: id, 8 bit: what we've seen so far are 1a, c6 +- I: id, 8 bit: factory (hard)coded random ID - B: battery, 4 bit: 0x8 if normal, 0x0 if low - T: temperature, 12 bit: 2's complement, scaled by 10 - F: 4 bit: seems to be 0xf constantly, a separator between temp and rain -- R: rain sensor, probably the remaining 24 bit: a counter for every 0.3mm of rain +- R: rain sensor, probably the remaining 24 bit: a counter for every 0.3 mm (4-LD5661) or 0.242 mm (4-LD6313) */ @@ -52,11 +53,17 @@ static int auriol_4ld5661_decode(r_device *decoder, bitbuffer_t *bitbuffer) int rain_raw = (b[4] << 12) | (b[5] << 4) | b[6] >> 4; - /* The display unit which comes with this device, multiplies gauge tip counts by 0.3 mm, which seems + /* The display unit which comes with this devices, multiplies gauge tip counts by 0.3 mm, which seems to be very inaccurate. We did a lot of measurements, the gauge's capacity is about 7.5 ml, the rain collection surface diameter is 96mm, 7.5 ml /((9.6 cm/2)^2*pi) ~= 1 mm of rain. Therefore we decided to correct this multiplier. - See also: https://github.com/merbanan/rtl_433/issues/1837 + The rain bucket tips at 7.2 ml for 4-LS6313. The main unit counts 0.242 mm per sensor tips. + The physical parameters are same. The calculation and the result is similar: + 7.2 ml / ((96 mm / 2)^2 * pi) ~= 1 mm (more exactly 0.995 mm). + Similar calculation is valid for 4-LD5972 (See PR #2633). + See also: + https://github.com/merbanan/rtl_433/issues/1837 + https://github.com/merbanan/rtl_433/pull/2633 */ float rain = rain_raw * 1.0F; @@ -67,6 +74,7 @@ static int auriol_4ld5661_decode(r_device *decoder, bitbuffer_t *bitbuffer) "battery_ok", "Battery OK", DATA_INT, batt_ok, "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, "rain_mm", "Rain", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain, + "rain", "Rain tips", DATA_INT, rain_raw, NULL); /* clang-format on */ @@ -83,11 +91,12 @@ static char const *const output_fields[] = { "battery_ok", "temperature_C", "rain_mm", + "rain", NULL, }; r_device const auriol_4ld5661 = { - .name = "Auriol 4-LD5661 temperature/rain sensor", + .name = "Auriol 4-LD5661/4-LD5972/4-LD6313 temperature/rain sensors", .modulation = OOK_PULSE_PPM, .short_width = 1000, .long_width = 2000, diff --git a/src/rtl_433/devices/blyss.c b/src/rtl_433/devices/blyss.c index b75aad2a..6ab62d02 100644 --- a/src/rtl_433/devices/blyss.c +++ b/src/rtl_433/devices/blyss.c @@ -13,7 +13,7 @@ Generic remote Blyss DC5-UK-WH as sold by B&Q. DC5-UK-WH pair with receivers, the codes used may be specific to a receiver - use with caution -warmup pulse 5552 us, 2072 gap +warm-up pulse 5552 us, 2072 gap short is 512 us pulse, 1484 us gap long is 1508 us pulse, 488 us gap packet gap is 6964 us @@ -23,15 +23,11 @@ packet gap is 6964 us static int blyss_callback(r_device *decoder, bitbuffer_t *bitbuffer) { - data_t *data; - uint8_t *b; - char id_str[16]; - for (int i = 0; i < bitbuffer->num_rows; ++i) { if (bitbuffer->bits_per_row[i] != 33) // last row is 32 continue; // DECODE_ABORT_LENGTH - b = bitbuffer->bb[i]; + uint8_t *b = bitbuffer->bb[i]; //This needs additional validation, but works on mine. Suspect each DC5-UK-WH uses different codes as the transmitter //is paired to the receivers to avoid being triggered by the neighbours transmitter ?!? @@ -40,10 +36,11 @@ static int blyss_callback(r_device *decoder, bitbuffer_t *bitbuffer) ((b[0] != 0xe7) || (b[1] != 0x37) || (b[2] != 0x7a) || (b[3] != 0x2c) || (b[4] != 0x80))) continue; // DECODE_ABORT_EARLY - sprintf(id_str, "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]); + char id_str[16]; + snprintf(id_str, sizeof(id_str), "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Blyss-DC5ukwh", "id", "", DATA_STRING, id_str, NULL); diff --git a/src/rtl_433/devices/bresser_5in1.c b/src/rtl_433/devices/bresser_5in1.c index 8a290f40..a079de01 100644 --- a/src/rtl_433/devices/bresser_5in1.c +++ b/src/rtl_433/devices/bresser_5in1.c @@ -42,7 +42,7 @@ Packet payload without preamble (203 bits): ef a1 ff ff 1f ff ef dc ff de df ff 7f 10 5e 00 00 e0 00 10 23 00 21 20 00 80 00 00 (low batt +ve temp) ed a1 ff ff 1f ff ef 8f ff d6 df ff 77 12 5e 00 00 e0 00 10 70 00 29 20 00 88 00 00 (low batt -ve temp -7.0C) ec 91 ff ff 1f fb ef e7 fe ad ed ff f7 13 6e 00 00 e0 04 10 18 01 52 12 00 08 00 00 (good batt -ve temp) - CC CC CC CC CC CC CC CC CC CC CC CC CC uu II SS GG DG WW W TT T HH RR RR Bt + CC CC CC CC CC CC CC CC CC CC CC CC CC uu II sS GG DG WW W TT T HH RR RR Bt G-MSB ^ ^ W-MSB (strange but consistent order) - C = Check, inverted data of 13 byte further @@ -56,6 +56,7 @@ Packet payload without preamble (203 bits): - H = humidity in percent, BCD coded, HH = 23 => 23 % - R = rain in mm, BCD coded, RRRR = 1203 => 031.2 mm - B = Battery. 0=Ok, 8=Low. +- s = startup, 0 after power-on/reset / 8 after 1 hour - S = sensor type, only low nibble used, 0x9 for Bresser Professional Rain Gauge */ diff --git a/src/rtl_433/devices/bresser_6in1.c b/src/rtl_433/devices/bresser_6in1.c index bd17c6e6..b8936c81 100644 --- a/src/rtl_433/devices/bresser_6in1.c +++ b/src/rtl_433/devices/bresser_6in1.c @@ -21,6 +21,7 @@ Decoder for Bresser Weather Center 6-in-1. - also Bresser 3-in-1 Professional Wind Gauge / Anemometer, PN 7002531 - also Bresser soil temperature and moisture meter, PN 7009972 - also Bresser Thermo-/Hygro-Sensor 7 Channel 868 MHz, PN 7009999 +- also Bresser Pool / Spa Thermometer, PN 7009973 (STYPE = 3) There are at least two different message types: - 24 seconds interval for temperature, hum, uv and rain (alternating messages) @@ -32,7 +33,7 @@ Also Bresser Explore Scientific SM60020 Soil moisture Sensor. Moisture: f16e 187000e34 7 ffffff0000 252 2 16 fff 004 000 [25,2, 99%, CH 7] - DIGEST:8h8h ID?8h8h8h8h STYPE:4h STARTUP:1b CH:3d 8h 8h8h 8h8h TEMP:12h ?2b BATT:1b ?1b MOIST:8h UV?~12h ?4h CHKSUM:8h + DIGEST:8h8h ID?8h8h8h8h STYPE:4h STARTUP:1b CH:3d 8h 8h8h 8h8h TEMP:12h TSIGN:1b ?1b BATT:1b ?1b MOIST:8h UV?~12h ?4h CHKSUM:8h Moisture is transmitted in the humidity field as index 1-16: 0, 7, 13, 20, 27, 33, 40, 47, 53, 60, 67, 73, 80, 87, 93, 99. The Wind speed and direction fields decode to valid zero but we exclude them from the output. @@ -70,7 +71,7 @@ The Wind speed and direction fields decode to valid zero but we exclude them fro Wind and Temperature/Humidity or Rain: - DIGEST:8h8h ID:8h8h8h8h STYPE:4h STARTUP:1b CH:3d WSPEED:~8h~4h ~4h~8h WDIR:12h ?4h TEMP:8h.4h ?2b BATT:1b ?1b HUM:8h UV?~12h ?4h CHKSUM:8h + DIGEST:8h8h ID:8h8h8h8h STYPE:4h STARTUP:1b CH:3d WSPEED:~8h~4h ~4h~8h WDIR:12h ?4h TEMP:8h.4h TSIGN:1b ?1b BATT:1b ?1b HUM:8h UV?~12h ?4h CHKSUM:8h DIGEST:8h8h ID:8h8h8h8h STYPE:4h STARTUP:1b CH:3d WSPEED:~8h~4h ~4h~8h WDIR:12h ?4h RAINFLAG:8h RAIN:8h8h UV:8h8h CHKSUM:8h Digest is LFSR-16 gen 0x8810 key 0x5412, excluding the add-checksum and trailer. @@ -137,17 +138,23 @@ static int bresser_6in1_decode(r_device *decoder, bitbuffer_t *bitbuffer) } uint32_t id = ((uint32_t)msg[2] << 24) | (msg[3] << 16) | (msg[4] << 8) | (msg[5]); - int s_type = (msg[6] >> 4); // 1: weather station, 2: indoor?, 4: soil probe + int s_type = (msg[6] >> 4); // 1: weather station, 2: indoor?, 3: pool thermometer, 4: soil probe int startup = (msg[6] >> 3) & 1; // s.a. #1214 int chan = (msg[6] & 0x7); int battery = (msg[13] >> 1) & 1; // b[13] & 0x02 is battery_good, s.a. #1993 // temperature, humidity, shared with rain counter, only if valid BCD digits - int temp_ok = msg[12] <= 0x99 && (msg[13] & 0xf0) <= 0x90; - int temp_raw = (msg[12] >> 4) * 100 + (msg[12] & 0x0f) * 10 + (msg[13] >> 4); - float temp_c = temp_raw * 0.1f; - if (temp_raw > 600) + int temp_ok = msg[12] <= 0x99 && (msg[13] & 0xf0) <= 0x90; + int temp_raw = (msg[12] >> 4) * 100 + (msg[12] & 0x0f) * 10 + (msg[13] >> 4); + int temp_sign = (msg[13] >> 3) & 1; + float temp_c = temp_raw * 0.1f; + if (temp_sign) { temp_c = (temp_raw - 1000) * 0.1f; + } + // Correction for Bresser 3-in-1 Professional Wind Gauge, PN 7002531 + if (temp_c < -50.0) { + temp_c = -temp_raw * 0.1f; + } int humidity = (msg[14] >> 4) * 10 + (msg[14] & 0x0f); diff --git a/src/rtl_433/devices/bresser_7in1.c b/src/rtl_433/devices/bresser_7in1.c index 411c264d..71f6c7db 100644 --- a/src/rtl_433/devices/bresser_7in1.c +++ b/src/rtl_433/devices/bresser_7in1.c @@ -11,18 +11,25 @@ #include "decoder.h" +#define SENSOR_TYPE_WEATHER 1 +#define SENSOR_TYPE_AIR_PM 8 + /** -Decoder for Bresser Weather Center 7-in-1, outdoor sensor. +Decoder for Bresser Weather Center 7-in-1 and Air Quality PM2.5 / PM10, outdoor sensors. -See https://github.com/merbanan/rtl_433/issues/1492 +See +https://github.com/merbanan/rtl_433/issues/1492 +and +https://github.com/merbanan/rtl_433/issues/2693 Preamble: aa aa aa aa aa 2d d4 Observed length depends on reset_limit. -The data has a whitening of 0xaa. +The data (not including STYPE, STARTUP, CH and maybe ID) has a whitening of 0xaa. +Weather Center Data layout: {271}631d05c09e9a18abaabaaaaaaaaa8adacbacff9cafcaaaaaaa000000000000000000 @@ -41,6 +48,11 @@ Data layout: Unit of light is kLux (not W/m²). +Air Quality Sensor PM2.5 / PM10 Sensor (PN 7009970) +Data layout: + + DIGEST:8h8h ID?8h8h ?8h8h STYPE:4h STARTUP:1b CH:3b ?8h 4h ?4h8h4h PM_2_5:4h8h4h PM10:4h8h4h ?4h ?8h4h BATT:1b ?3b ?8h8h8h8h8h8h TRAILER:8h8h8h + First two bytes are an LFSR-16 digest, generator 0x8810 key 0xba95 with a final xor 0x6df1, which likely means we got that wrong. */ @@ -76,6 +88,11 @@ static int bresser_7in1_decode(r_device *decoder, bitbuffer_t *bitbuffer) if (msg[21] == 0x00) { return DECODE_FAIL_SANITY; } + + int s_type = msg[6] >> 4; + int nstartup = (msg[6] & 0x08) >> 3; + int chan = msg[6] & 0x07; + // data whitening for (unsigned i = 0; i < sizeof (msg); ++i) { msg[i] ^= 0xaa; @@ -90,53 +107,87 @@ static int bresser_7in1_decode(r_device *decoder, bitbuffer_t *bitbuffer) return DECODE_FAIL_MIC; } - int id = (msg[2] << 8) | (msg[3]); - int wdir = (msg[4] >> 4) * 100 + (msg[4] & 0x0f) * 10 + (msg[5] >> 4); - int wgst_raw = (msg[7] >> 4) * 100 + (msg[7] & 0x0f) * 10 + (msg[8] >> 4); - int wavg_raw = (msg[8] & 0x0f) * 100 + (msg[9] >> 4) * 10 + (msg[9] & 0x0f); - int rain_raw = (msg[10] >> 4) * 100000 + (msg[10] & 0x0f) * 10000 + (msg[11] >> 4) * 1000 - + (msg[11] & 0x0f) * 100 + (msg[12] >> 4) * 10 + (msg[12] & 0x0f) * 1; // 6 digits - float rain_mm = rain_raw * 0.1f; - int temp_raw = (msg[14] >> 4) * 100 + (msg[14] & 0x0f) * 10 + (msg[15] >> 4); - float temp_c = temp_raw * 0.1f; - int flags = (msg[15] & 0x0f); + int id = (msg[2] << 8) | (msg[3]); + int flags = (msg[15] & 0x0f); int battery_low = (flags & 0x06) == 0x06; - if (temp_raw > 600) - temp_c = (temp_raw - 1000) * 0.1f; - int humidity = (msg[16] >> 4) * 10 + (msg[16] & 0x0f); - int lght_raw = (msg[17] >> 4) * 100000 + (msg[17] & 0x0f) * 10000 + (msg[18] >> 4) * 1000 - + (msg[18] & 0x0f) * 100 + (msg[19] >> 4) * 10 + (msg[19] & 0x0f); - int uv_raw = (msg[20] >> 4) * 100 + (msg[20] & 0x0f) * 10 + (msg[21] >> 4); - - float light_klx = lght_raw * 0.001f; // TODO: remove this - float light_lux = lght_raw; - float uv_index = uv_raw * 0.1f; - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Bresser-7in1", - "id", "", DATA_INT, id, - "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_INT, humidity, - "wind_max_m_s", "Wind Gust", DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wgst_raw * 0.1f, - "wind_avg_m_s", "Wind Speed", DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wavg_raw * 0.1f, - "wind_dir_deg", "Direction", DATA_INT, wdir, - "rain_mm", "Rain", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, - "light_klx", "Light", DATA_FORMAT, "%.3f klx", DATA_DOUBLE, light_klx, // TODO: remove this - "light_lux", "Light", DATA_FORMAT, "%.3f lux", DATA_DOUBLE, light_lux, - "uv", "UV Index", DATA_FORMAT, "%.1f", DATA_DOUBLE, uv_index, - "battery_ok", "Battery", DATA_INT, !battery_low, - "mic", "Integrity", DATA_STRING, "CRC", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - return 1; + + if (s_type == SENSOR_TYPE_WEATHER) { + int wdir = (msg[4] >> 4) * 100 + (msg[4] & 0x0f) * 10 + (msg[5] >> 4); + int wgst_raw = (msg[7] >> 4) * 100 + (msg[7] & 0x0f) * 10 + (msg[8] >> 4); + int wavg_raw = (msg[8] & 0x0f) * 100 + (msg[9] >> 4) * 10 + (msg[9] & 0x0f); + int rain_raw = (msg[10] >> 4) * 100000 + (msg[10] & 0x0f) * 10000 + (msg[11] >> 4) * 1000 + + (msg[11] & 0x0f) * 100 + (msg[12] >> 4) * 10 + (msg[12] & 0x0f) * 1; // 6 digits + float rain_mm = rain_raw * 0.1f; + int temp_raw = (msg[14] >> 4) * 100 + (msg[14] & 0x0f) * 10 + (msg[15] >> 4); + float temp_c = temp_raw * 0.1f; + + if (temp_raw > 600) + temp_c = (temp_raw - 1000) * 0.1f; + int humidity = (msg[16] >> 4) * 10 + (msg[16] & 0x0f); + int lght_raw = (msg[17] >> 4) * 100000 + (msg[17] & 0x0f) * 10000 + (msg[18] >> 4) * 1000 + + (msg[18] & 0x0f) * 100 + (msg[19] >> 4) * 10 + (msg[19] & 0x0f); + int uv_raw = (msg[20] >> 4) * 100 + (msg[20] & 0x0f) * 10 + (msg[21] >> 4); + + float light_klx = lght_raw * 0.001f; // TODO: remove this + float light_lux = lght_raw; + float uv_index = uv_raw * 0.1f; + + /* clang-format off */ + data = data_make( + "model", "", DATA_STRING, "Bresser-7in1", + "id", "", DATA_INT, id, + "startup", "Startup", DATA_COND, !nstartup, DATA_INT, !nstartup, + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, + "humidity", "Humidity", DATA_INT, humidity, + "wind_max_m_s", "Wind Gust", DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wgst_raw * 0.1f, + "wind_avg_m_s", "Wind Speed", DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wavg_raw * 0.1f, + "wind_dir_deg", "Direction", DATA_INT, wdir, + "rain_mm", "Rain", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, + "light_klx", "Light", DATA_FORMAT, "%.3f klx", DATA_DOUBLE, light_klx, // TODO: remove this + "light_lux", "Light", DATA_FORMAT, "%.3f lux", DATA_DOUBLE, light_lux, + "uv", "UV Index", DATA_FORMAT, "%.1f", DATA_DOUBLE, uv_index, + "battery_ok", "Battery", DATA_INT, !battery_low, + "mic", "Integrity", DATA_STRING, "CRC", + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; + + } else if (s_type == SENSOR_TYPE_AIR_PM) { + int pm_2_5 = (msg[10] & 0x0f) * 1000 + (msg[11] >> 4) * 100 + (msg[11] & 0x0f) * 10 + (msg[12] >> 4); + int pm_10 = (msg[12] & 0x0f) * 1000 + (msg[13] >> 4) * 100 + (msg[13] & 0x0f) * 10 + (msg[14] >> 4); + + // To Do: identify further data + + /* clang-format off */ + data = data_make( + "model", "", DATA_STRING, "Bresser-7in1", + "id", "", DATA_INT, id, + "channel", "", DATA_INT, chan, + "startup", "Startup", DATA_COND, !nstartup, DATA_INT, !nstartup, + "battery_ok", "Battery", DATA_INT, !battery_low, + "pm_2_5_ug_m3", "PM2.5 Mass Concentration", DATA_INT, pm_2_5, + "pm_10_ug_m3", "PM10 Mass Concentraton", DATA_INT, pm_10, + "mic", "Integrity", DATA_STRING, "CRC", + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; + + } else { + decoder_logf(decoder, 2, __func__, "DECODE_FAIL_SANITY, s_type=%d not implemented", s_type); + return DECODE_FAIL_SANITY; + + } } static char const *const output_fields[] = { "model", "id", + "channel", + "startup", "temperature_C", "humidity", "wind_max_m_s", @@ -146,13 +197,15 @@ static char const *const output_fields[] = { "light_klx", // TODO: remove this "light_lux", "uv", + "pm_2_5_ug_m3", + "pm_10_ug_m3", "battery_ok", "mic", NULL, }; r_device const bresser_7in1 = { - .name = "Bresser Weather Center 7-in-1", + .name = "Bresser Weather Center 7-in-1, Air Quality PM2.5 / PM10", .modulation = FSK_PULSE_PCM, .short_width = 124, .long_width = 124, diff --git a/src/rtl_433/devices/bresser_leakage.c b/src/rtl_433/devices/bresser_leakage.c new file mode 100644 index 00000000..4cc8bc41 --- /dev/null +++ b/src/rtl_433/devices/bresser_leakage.c @@ -0,0 +1,158 @@ +/** @file + Bresser Water Leakage Sensor. + + Copyright (C) 2023 Matthias Prinke + + 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. +*/ + +#include "decoder.h" + +#define SENSOR_TYPE_LEAKAGE 5 + +/** +Bresser Water Leakage Sensor. + +Decoder for Bresser Water Leakage outdoor sensor, PN 7009975 + +see https://github.com/merbanan/rtl_433/issues/2576 + +Based on bresser_6in1.c + +Preamble: aa aa 2d d4 + +Data layout: + + CCCCCCCC CCCCCCCC IIIIIIII IIIIIIII IIIIIIII IIIIIIII SSSSQHHH ANBBFFFF + +- C: 16-bit, crc16/xmodem, polynomial: 0x1021, init: 0x0000, range: byte 2...6 +- I: 24-bit little-endian id; changes on power-up/reset +- S: 4 bit sensor type +- Q: 1 bit startup; changes from 0 to 1 approx. one hour after power-on/reset +- H: 3 bit channel; set via switch on the device, latched at power-on/reset +- A: 1 bit alarm +- N: 1 bit no_alarm; inverse of alarm +- B: 2 bit battery state; 0b11 if battery is o.k. +- F: 4 bit flags (always 0b0000) + + +Examples: + + [Bresser Water Leakage Sensor, PN 7009975] + + [00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25] + + C7 70 35 97 04 08 57 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF FF FF FF FF [CH7] + DF 7D 36 49 27 09 56 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF FF FF FF FF [CH6] + 9E 30 79 84 33 06 55 70 00 00 00 00 00 00 00 00 03 FF FD DF FF BF FF DF FF FF [CH5] + E2 C8 68 27 91 24 54 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF FF FF FF FF [CH4] + B3 DA 55 57 17 40 53 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF FF FF FF FB [CH3] + 37 FA 84 73 03 02 52 70 00 00 00 00 00 00 00 00 03 FF FF FF DF FF FF FF FF FF [CH2] + 27 F3 80 02 52 88 51 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF DF FF FF FF [CH1] + A6 FB 80 02 52 88 59 70 00 00 00 00 00 00 00 00 03 FD F7 FF FF BF FF FF FF FF [CH1+NSTARTUP] + A6 FB 80 02 52 88 59 B0 00 00 00 00 00 00 00 00 03 FF FF FF FD FF F7 FF FF FF [CH1+NSTARTUP+ALARM] + A6 FB 80 02 52 88 59 70 00 00 00 00 00 00 00 00 03 FF FF BF F7 F7 FD 7F FF FF [CH1+NSTARTUP] + [Reset] + C0 10 36 79 37 09 51 70 00 00 00 00 00 00 00 00 01 1E FD FD FF FF FF DF FF FF [CH1] + C0 10 36 79 37 09 51 B0 00 00 00 00 00 00 00 00 03 FE FD FF AF FF FF FF FF FD [CH1+ALARM] + [Reset] + 71 9C 54 81 72 09 51 40 00 00 00 00 00 00 00 00 0F FF FF FF FF FF FF DF FF FE [CH1+BATT_LO] + 71 9C 54 81 72 09 51 40 00 00 00 00 00 00 00 00 0F FE FF FF FF FF FB FF FF FF + 71 9C 54 81 72 09 51 40 00 00 00 00 00 00 00 00 07 FD F7 FF DF FF FF DF FF FF + 71 9C 54 81 72 09 51 80 00 00 00 00 00 00 00 00 1F FF FF F7 FF FF FF FF FF FF [CH1+BATT_LO+ALARM] + F0 94 54 81 72 09 59 40 00 00 00 00 00 00 00 00 0F FF DF FF FF FF FF BF FD F7 [CH1+BATT_LO+NSTARTUP] + F0 94 54 81 72 09 59 80 00 00 00 00 00 00 00 00 03 FF B7 FF ED FF FF FF DF FF [CH1+BATT_LO+NSTARTUP+ALARM] + + */ + +static int bresser_leakage_decode(r_device *decoder, bitbuffer_t *bitbuffer) +{ + uint8_t const preamble_pattern[] = {0xaa, 0xaa, 0x2d, 0xd4}; + uint8_t msg[18]; + + if (bitbuffer->num_rows != 1 + || bitbuffer->bits_per_row[0] < 160 + || bitbuffer->bits_per_row[0] > 440) { + decoder_logf(decoder, 2, __func__, "bit_per_row %u out of range", bitbuffer->bits_per_row[0]); + return DECODE_ABORT_EARLY; // 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_ABORT_LENGTH; + } + start_pos += sizeof (preamble_pattern) * 8; + + unsigned len = bitbuffer->bits_per_row[0] - start_pos; + if (len < sizeof(msg) * 8) { + decoder_logf(decoder, 2, __func__, "%u too short", len); + return DECODE_ABORT_LENGTH; // message too short + } + + bitbuffer_extract_bytes(bitbuffer, 0, start_pos, msg, sizeof(msg) * 8); + + decoder_log_bitrow(decoder, 2, __func__, msg, sizeof(msg) * 8, ""); + + // CRC check + uint16_t crc_calculated = crc16(&msg[2], 5, 0x1021, 0x0000); + uint16_t crc_received = msg[0] << 8 | msg[1]; + decoder_logf(decoder, 2, __func__, "CRC 0x%04X = 0x%04X", crc_calculated, crc_received); + if (crc_received != crc_calculated) { + decoder_logf(decoder, 1, __func__, "CRC check failed (0x%04X != 0x%04X)", crc_calculated, crc_received); + return DECODE_FAIL_MIC; + } + + uint32_t sensor_id = ((uint32_t)msg[2] << 24) | (msg[3] << 16) | (msg[4] << 8) | (msg[5]); + int s_type = msg[6] >> 4; + int chan = (msg[6] & 0x7); + int battery_ok = ((msg[7] & 0x30) != 0x00); + int nstartup = (msg[6] & 0x08) >> 3; + int alarm = (msg[7] & 0x80) >> 7; + int no_alarm = (msg[7] & 0x40) >> 6; + + // Sanity checks + if (s_type != SENSOR_TYPE_LEAKAGE + || alarm == no_alarm + || chan == 0) { + return DECODE_FAIL_SANITY; + } + + /* clang-format off */ + data_t *data = data_make( + "model", "", DATA_STRING, "Bresser-Leakage", + "id", "", DATA_FORMAT, "%08x", DATA_INT, sensor_id, + "channel", "", DATA_INT, chan, + "battery_ok", "Battery", DATA_INT, battery_ok, + "alarm", "Alarm", DATA_INT, alarm, + "startup", "Startup", DATA_COND, !nstartup, DATA_INT, !nstartup, + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; +} + +static char const *const output_fields[] = { + "model", + "id", + "channel", + "battery_ok", + "alarm", + "startup", + NULL, +}; + +r_device const bresser_leakage = { + .name = "Bresser water leakage", + .modulation = FSK_PULSE_PCM, + .short_width = 124, + .long_width = 124, + .reset_limit = 25000, + .decode_fn = &bresser_leakage_decode, + .fields = output_fields, +}; diff --git a/src/rtl_433/devices/bresser_lightning.c b/src/rtl_433/devices/bresser_lightning.c new file mode 100644 index 00000000..24ce8f29 --- /dev/null +++ b/src/rtl_433/devices/bresser_lightning.c @@ -0,0 +1,136 @@ +/** @file + Bresser Lightning Sensor. + + Copyright (C) 2023 The rtl_433 Project + + 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. +*/ + +#include "decoder.h" + +#define SENSOR_TYPE_LIGHTNING 9 + +/** +Bresser Lightning Sensor. + +Decoder for Bresser lightning outdoor sensor, PN 7009976 + +see https://github.com/merbanan/rtl_433/issues/2140 + +Preamble: aa aa 2d d4 + +Data layout: + DIGEST:8h8h ID:8h8h CTR:12h BATT:1b ?3b STYPE:4h STARTUP:1b CH:3d KM:8d ?8h8h + +Based on bresser_7in1.c + +The data (not including STYPE, STARTUP, CH and maybe ID) has a whitening of 0xaa. +CH is always 0. + +First two bytes are an LFSR-16 digest, generator 0x8810 key 0xabf9 with a final xor 0x899e +*/ + +static int bresser_lightning_decode(r_device *decoder, bitbuffer_t *bitbuffer) +{ + uint8_t const preamble_pattern[] = {0xaa, 0xaa, 0x2d, 0xd4}; + uint8_t msg[25]; + + /* clang-format off */ + if ( bitbuffer->num_rows != 1 + || bitbuffer->bits_per_row[0] < 160 + || bitbuffer->bits_per_row[0] > 440) { + decoder_logf(decoder, 2, __func__, "bit_per_row %u out of range", bitbuffer->bits_per_row[0]); + return DECODE_ABORT_EARLY; // Unrecognized data + } + /* clang-format on */ + + unsigned start_pos = bitbuffer_search(bitbuffer, 0, 0, + preamble_pattern, sizeof(preamble_pattern) * 8); + + if (start_pos >= bitbuffer->bits_per_row[0]) { + return DECODE_ABORT_LENGTH; + } + start_pos += sizeof(preamble_pattern) * 8; + + unsigned len = bitbuffer->bits_per_row[0] - start_pos; + if (len < sizeof(msg) * 8) { + decoder_logf(decoder, 2, __func__, "%u too short", len); + return DECODE_ABORT_LENGTH; // message too short + } + + bitbuffer_extract_bytes(bitbuffer, 0, start_pos, msg, sizeof(msg) * 8); + + decoder_log_bitrow(decoder, 2, __func__, msg, sizeof(msg) * 8, "MSG"); + + int s_type = msg[6] >> 4; + int chan = msg[6] & 0x07; + int battery_low = (msg[5] & 0x08) >> 3; + int nstartup = (msg[6] & 0x08) >> 3; + + // data de-whitening + for (unsigned i = 0; i < sizeof(msg); ++i) { + msg[i] ^= 0xaa; + } + decoder_log_bitrow(decoder, 2, __func__, msg, sizeof(msg) * 8, "XOR"); + + // LFSR-16 digest, generator 0x8810 key 0xba95 final xor 0x6df1 + int chk = (msg[0] << 8) | msg[1]; + int digest = lfsr_digest16(&msg[2], 23, 0x8810, 0xba95); + if ((chk ^ digest) != 0x6df1) { + decoder_logf(decoder, 2, __func__, "Digest check failed %04x vs %04x (%04x)", chk, digest, chk ^ digest); + return DECODE_FAIL_MIC; + } + + int sensor_id = (msg[2] << 8) | (msg[3]); + int distance_km = msg[7]; + int count = (msg[4] << 4) | (msg[5] & 0xf0) >> 4; + int unknown1 = ((msg[5] & 0x0f) << 8) | msg[6]; + int unknown2 = (msg[8] << 8) | msg[9]; + + // Sanity checks + if ((s_type != SENSOR_TYPE_LIGHTNING) || (chan != 0)) { + return DECODE_FAIL_SANITY; + } + + /* clang-format off */ + data_t *data = data_make( + "model", "", DATA_STRING, "Bresser-Lightning", + "id", "", DATA_FORMAT, "%08x", DATA_INT, sensor_id, + "startup", "Startup", DATA_COND, !nstartup, DATA_INT, !nstartup, + "battery_ok", "Battery", DATA_INT, !battery_low, + "distance_km", "storm_distance_km", DATA_INT, distance_km, + "strike_count", "strike_count", DATA_INT, count, + "unknown1", "Unknown1", DATA_FORMAT, "%08x", DATA_INT, unknown1, + "unknown2", "Unknown2", DATA_FORMAT, "%08x", DATA_INT, unknown2, + "mic", "Integrity", DATA_STRING, "CRC", + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; +} + +static char const *const output_fields[] = { + "model", + "id", + "startup", + "battery_ok", + "distance_km", + "strike_count", + "unknown1", + "unknown2", + NULL, +}; + +r_device const bresser_lightning = { + .name = "Bresser lightning", + .modulation = FSK_PULSE_PCM, + .short_width = 124, + .long_width = 124, + .reset_limit = 25000, + .decode_fn = &bresser_lightning_decode, + .fields = output_fields, +}; diff --git a/src/rtl_433/devices/burnhardbbq.c b/src/rtl_433/devices/burnhardbbq.c index 3cd88bc4..222c35da 100644 --- a/src/rtl_433/devices/burnhardbbq.c +++ b/src/rtl_433/devices/burnhardbbq.c @@ -71,7 +71,7 @@ static int burnhardbbq_decode(r_device *decoder, bitbuffer_t *bitbuffer) float temp_c = (temp_raw - 500) * 0.1f; char timer_str[6]; - sprintf(timer_str, "%02x:%02x", b[3], b[4] & 0x7f); + snprintf(timer_str, sizeof(timer_str), "%02x:%02x", b[3], b[4] & 0x7f); char const *meat; switch (b[5] >> 4) { diff --git a/src/rtl_433/devices/chuango.c b/src/rtl_433/devices/chuango.c index 18bae7ab..9bac0225 100644 --- a/src/rtl_433/devices/chuango.c +++ b/src/rtl_433/devices/chuango.c @@ -12,17 +12,18 @@ /** Chuango Security Technology. -likely based on HS1527 or compatible +Likely based on HS1527 or compatible Tested devices: -G5 GSM/SMS/RFID Touch Alarm System (Alarm, Disarm, ...) -DWC-100 Door sensor (Default: Normal Zone) -DWC-102 Door sensor (Default: Normal Zone) -KP-700 Wireless Keypad (Arm, Disarm, Home Mode, Alarm!) -PIR-900 PIR sensor (Default: Home Mode Zone) -RC-80 Remote Control (Arm, Disarm, Home Mode, Alarm!) -SMK-500 Smoke sensor (Default: 24H Zone) -WI-200 Water sensor (Default: 24H Zone) +- G5 GSM/SMS/RFID Touch Alarm System (Alarm, Disarm, ...) +- DWC-100 Door sensor (Default: Normal Zone) +- DWC-102 Door sensor (Default: Normal Zone) +- KP-700 Wireless Keypad (Arm, Disarm, Home Mode, Alarm!) +- PIR-900 PIR sensor (Default: Home Mode Zone) +- RC-80 Remote Control (Arm, Disarm, Home Mode, Alarm!) +- SMK-500 Smoke sensor (Default: 24H Zone) +- WI-200 Water sensor (Default: 24H Zone) +- newer DWC-102 additionally generates a cmd=12 signal on door/windows being closed Note: simple 24 bit fixed ID protocol (x1527 style) and should be handled by the flex decoder. @@ -58,7 +59,7 @@ static int chuango_callback(r_device *decoder, bitbuffer_t *bitbuffer) case 0xF: cmd_str = "?"; break; case 0xE: cmd_str = "?"; break; case 0xD: cmd_str = "Low Battery"; break; - case 0xC: cmd_str = "?"; break; + case 0xC: cmd_str = "Closing"; break; case 0xB: cmd_str = "24H Zone"; break; case 0xA: cmd_str = "Single Delay Zone"; break; case 0x9: cmd_str = "?"; break; diff --git a/src/rtl_433/devices/cotech_36_7959.c b/src/rtl_433/devices/cotech_36_7959.c index ac793fbd..da7358cc 100644 --- a/src/rtl_433/devices/cotech_36_7959.c +++ b/src/rtl_433/devices/cotech_36_7959.c @@ -9,6 +9,8 @@ (at your option) any later version. */ +#include "decoder.h" + /** Cotech 36-7959 Weatherstation, 433Mhz. @@ -47,9 +49,6 @@ Message layout - X : 8 bit: CRC, poly 0x31, init 0xc0 */ -#include -#include "decoder.h" - static int cotech_36_7959_decode(r_device *decoder, bitbuffer_t *bitbuffer) { uint8_t const preamble[] = {0x01, 0x40}; // 12 bits @@ -109,7 +108,7 @@ static int cotech_36_7959_decode(r_device *decoder, bitbuffer_t *bitbuffer) float temp_c = (temp_raw - 400) * 0.1f; // On models without a light sensor, the value read for UV index is out of bounds with its top bits set - bool light_is_valid = ((uv & 0xf0) == 0); + int light_is_valid = ((uv & 0xf0) == 0); /* clang-format off */ data = data_make( diff --git a/src/rtl_433/devices/digitech_xc0324.c b/src/rtl_433/devices/digitech_xc0324.c index 38589189..ffc136d6 100644 --- a/src/rtl_433/devices/digitech_xc0324.c +++ b/src/rtl_433/devices/digitech_xc0324.c @@ -75,7 +75,7 @@ static int decode_xc0324_message(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, uint16_t bitpos, const int latest_event, data_t **data) { uint8_t b[XC0324_MESSAGE_BYTELEN]; - char id[4] = {0}; + char id[3] = {0}; double temperature; int humidity; uint8_t chksum; // == 0x00 for a good message @@ -101,7 +101,7 @@ static int decode_xc0324_message(r_device *decoder, bitbuffer_t *bitbuffer, } // Extract the id as hex string - snprintf(id, 3, "%02X", b[1]); + snprintf(id, sizeof(id), "%02X", b[1]); // Decode temperature (b[2]), plus 1st 4 bits b[3], LSB first order! // Tenths of degrees C, offset from the minimum possible (-40.0 degrees) diff --git a/src/rtl_433/devices/dsc.c b/src/rtl_433/devices/dsc.c index 2688cbca..5b6360a0 100644 --- a/src/rtl_433/devices/dsc.c +++ b/src/rtl_433/devices/dsc.c @@ -113,8 +113,6 @@ static int dsc_callback(r_device *decoder, bitbuffer_t *bitbuffer) uint8_t status, crc; //int subtype; uint32_t esn; - char status_str[3]; - char esn_str[7]; int s_closed, s_event, s_tamper, s_battery_low; int s_xactivity, s_xtamper1, s_xtamper2, s_exception; @@ -194,7 +192,7 @@ static int dsc_callback(r_device *decoder, bitbuffer_t *bitbuffer) // Tamper: 0x10 set or 0x01 unset indicate tamper // 0x10 Set to tamper message type (more testing needed) - // 0x01 Cleared tamper status (seend during hearbeats) + // 0x01 Cleared tamper status (seen during heartbeats) s_tamper = ((status & 0x01) != 0x01) || ((status & 0x10) == 0x10); // "experimental" (naming might change) @@ -208,8 +206,10 @@ static int dsc_callback(r_device *decoder, bitbuffer_t *bitbuffer) // 0x80 is always set and 0x04 has never been set. s_exception = ((status & 0x80) != 0x80) || ((status & 0x04) == 0x04); - sprintf(status_str, "%02x", status); - sprintf(esn_str, "%06x", esn); + char status_str[3]; + snprintf(status_str, sizeof(status_str), "%02x", status); + char esn_str[7]; + snprintf(esn_str, sizeof(esn_str), "%06x", esn); /* clang-format off */ data = data_make( diff --git a/src/rtl_433/devices/ecodhome.c b/src/rtl_433/devices/ecodhome.c index 7276fb51..f25e3c66 100644 --- a/src/rtl_433/devices/ecodhome.c +++ b/src/rtl_433/devices/ecodhome.c @@ -67,7 +67,7 @@ Data Seen: 52315c6a 7700 565a 007b00 52315c6a 7700 565a 007a00 -Removing the first 1 or 2 bits gives a prefix of 55a8aaaaaa2dd4, the leading bits are likely warmup or garbage. +Removing the first 1 or 2 bits gives a prefix of 55a8aaaaaa2dd4, the leading bits are likely warm-up or garbage. The next bytes of 5231 5c6a 7700 are likely a serial number (id). diff --git a/src/rtl_433/devices/efth800.c b/src/rtl_433/devices/efth800.c index 2c20f90f..0cfe2d52 100644 --- a/src/rtl_433/devices/efth800.c +++ b/src/rtl_433/devices/efth800.c @@ -54,8 +54,9 @@ static int eurochron_efth800_decode(r_device *decoder, bitbuffer_t *bitbuffer) uint8_t *b = bitbuffer->bb[row]; // 0 1 2 3 4 5 6 7 - // ?1b CH:3d ID:12d 2b H?6d 2b M:6d 2b S:6d Y?7d D:5d M:4d CHK?8h 1x - int dcf77_hour = (b[2] & 0x3f); + // ?1b CH:3d ID:12d 3b H?5d 2b M:6d 2b S:6d Y?7d D:5d M:4d CHK?8h 1x + // TODO: (b[2] >> 5) may have DST and/or TZ info ? + int dcf77_hour = (b[2] & 0x1f); int dcf77_min = (b[3] & 0x3f); int dcf77_sec = (b[4] & 0x3f); int dcf77_year = (b[5] >> 1); @@ -63,7 +64,7 @@ static int eurochron_efth800_decode(r_device *decoder, bitbuffer_t *bitbuffer) int dcf77_mth = (b[6] & 0x0f); if (!crc8(b, 8, 0x31, 0x00)) { - sprintf(dcf77_str, "%4d-%02d-%02dT%02d:%02d:%02d", dcf77_year + 2000, dcf77_mth, dcf77_day, dcf77_hour, dcf77_min, dcf77_sec); + snprintf(dcf77_str, sizeof(dcf77_str), "%4d-%02d-%02dT%02d:%02d:%02d", dcf77_year + 2000, dcf77_mth, dcf77_day, dcf77_hour, dcf77_min, dcf77_sec); } } diff --git a/src/rtl_433/devices/elro_db286a.c b/src/rtl_433/devices/elro_db286a.c index af6d56d8..c4ac3e37 100644 --- a/src/rtl_433/devices/elro_db286a.c +++ b/src/rtl_433/devices/elro_db286a.c @@ -36,7 +36,7 @@ static int elro_db286a_callback(r_device *decoder, bitbuffer_t *bitbuffer) // 32 bits, trailing bit is dropped char id_str[4 * 2 + 1]; - sprintf(id_str, "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]); + snprintf(id_str, sizeof(id_str), "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]); /* clang-format off */ data_t *data = data_make( diff --git a/src/rtl_433/devices/emax.c b/src/rtl_433/devices/emax.c index 74c5eada..2c3f39df 100644 --- a/src/rtl_433/devices/emax.c +++ b/src/rtl_433/devices/emax.c @@ -185,10 +185,10 @@ static int emax_decode(r_device *decoder, bitbuffer_t *bitbuffer) int temp_raw = ((b[4] & 0x0f) << 8) | (b[5]); // weird format float temp_f = (temp_raw - 900) * 0.1f; int humidity = b[6]; - int wind_raw = ((b[7] - 1) << 8 ) | (b[8] -1); // all default is 01 so need to remove 1 from all bytes. + int wind_raw = (((b[7] - 1) & 0xff) << 8) | ((b[8] - 1) & 0xff); // need to remove 1 from byte , 0x01 - 1 = 0 , 0x02 - 1 = 1 ... 0xff -1 = 254 , 0x00 - 1 = 255. float speed_kmh = wind_raw * 0.2f; - int direction_deg = (((b[9] - 1) & 0x0f) << 8) | (b[10] - 1); - int rain_raw = ((b[11] - 1) << 8 ) | (b[12] -1); + int direction_deg = (((b[9] - 1) & 0x0f) << 8) | ((b[10] - 1) & 0xff); + int rain_raw = (((b[11] - 1) & 0xff) << 8) | ((b[12] - 1) & 0xff); float rain_mm = rain_raw * 0.2f; if (b[29] == 0x17) { // with UV/Lux, without Wind Gust diff --git a/src/rtl_433/devices/emos_e6016.c b/src/rtl_433/devices/emos_e6016.c index f12bb7d6..f51b5ea6 100644 --- a/src/rtl_433/devices/emos_e6016.c +++ b/src/rtl_433/devices/emos_e6016.c @@ -21,18 +21,18 @@ EMOS E6016 weatherstation with DCF77. Data Layout: - PP PP PP II BK KK KK KK CT TT HH SS D? XX RR + PP PP PP II ?K KK KK KK CT TT HH SS DF XX RR - P: (24 bit) preamble - I: (8 bit) ID -- B: (2 bit) battery indication +- ?: (2 bit) unknown - K: (32 bit) datetime, fields are 6d-4d-5d 5d:6d:6d - C: (2 bit) channel - T: (12 bit) temperature, signed, scale 10 - H: (8 bit) humidity - S: (8 bit) wind speed - D: (4 bit) wind direction -- ?: (4 bit) unknown +- F: (4 bit) flags of (?B??), B is battery good indication - X: (8 bit) checksum - R: (8 bit) repeat counter @@ -47,11 +47,11 @@ Raw data: Format string: - MODEL?:8h8h8h ID?:8d BAT?2b DT:6d-4d-5dT5d:6d:6d CH:2d TEMP:12d HUM?8d WSPEED:8d WINDIR:4d ?4h CHK:8h REPEAT:8h + MODEL?:8h8h8h ID?:8d ?2b DT:6d-4d-5dT5d:6d:6d CH:2d TEMP:12d HUM?8d WSPEED:8d WINDIR:4d BAT:4b CHK:8h REPEAT:8h Decoded example: - MODEL?:aaa583 ID?:255 BAT?10 DT:21-05-21T07:49:35 CH:0 TEMP:0201 HUM?037 WSPEED:000 WINDIR:10 ?2 CHK:c7 REPEAT:00 + MODEL?:aaa583 ID?:255 ?10 DT:21-05-21T07:49:35 CH:0 TEMP:0201 HUM?037 WSPEED:000 WINDIR:10 BAT:1101 CHK:c7 REPEAT:00 */ @@ -86,7 +86,7 @@ static int emos_e6016_decode(r_device *decoder, bitbuffer_t *bitbuffer) } int id = b[3]; - int battery = (b[4] >> 6); + int battery = ((b[12] >> 2) & 0x1); unsigned dcf77 = ((b[4] & 0x3f) << 26) | (b[5] << 18) | (b[6] << 10) | (b[7] << 2) | (b[8] >> 6); int dcf77_sec = ((dcf77 >> 0) & 0x3f); int dcf77_min = ((dcf77 >> 6) & 0x3f); @@ -98,19 +98,19 @@ static int emos_e6016_decode(r_device *decoder, bitbuffer_t *bitbuffer) int temp_raw = (int16_t)(((b[8] & 0x0f) << 12) | (b[9] << 4)); // use sign extend float temp_c = (temp_raw >> 4) * 0.1f; int humidity = b[10]; - float speed_ms = b[11]; + float speed_ms = b[11] * 0.295; int dir_raw = (((b[12] & 0xf0) >> 4)); float dir_deg = dir_raw * 22.5f; char dcf77_str[20]; // "2064-16-32T32:64:64" - sprintf(dcf77_str, "%4d-%02d-%02dT%02d:%02d:%02d", dcf77_year + 2000, dcf77_mth, dcf77_day, dcf77_hour, dcf77_min, dcf77_sec); + snprintf(dcf77_str, sizeof(dcf77_str), "%4d-%02d-%02dT%02d:%02d:%02d", dcf77_year + 2000, dcf77_mth, dcf77_day, dcf77_hour, dcf77_min, dcf77_sec); /* clang-format off */ data_t *data = data_make( "model", "", DATA_STRING, "EMOS-E6016", "id", "House Code", DATA_INT, id, "channel", "Channel", DATA_INT, channel, - "battery_ok", "Battery_OK", DATA_INT, !!battery, + "battery_ok", "Battery_OK", DATA_INT, battery, "temperature_C", "Temperature_C", DATA_FORMAT, "%.1f", DATA_DOUBLE, temp_c, "humidity", "Humidity", DATA_FORMAT, "%u", DATA_INT, humidity, "wind_avg_m_s", "WindSpeed m_s", DATA_FORMAT, "%.1f", DATA_DOUBLE, speed_ms, diff --git a/src/rtl_433/devices/fineoffset.c b/src/rtl_433/devices/fineoffset.c index feabf8d6..f7aae57d 100644 --- a/src/rtl_433/devices/fineoffset.c +++ b/src/rtl_433/devices/fineoffset.c @@ -630,7 +630,7 @@ static int fineoffset_WH51_callback(r_device *decoder, bitbuffer_t *bitbuffer) // Decode data char id[7]; - sprintf(id, "%02x%02x%02x", b[1], b[2], b[3]); + snprintf(id, sizeof(id), "%02x%02x%02x", b[1], b[2], b[3]); int boost = (b[4] & 0xe0) >> 5; int battery_mv = (b[4] & 0x1f) * 100; float battery_level = (battery_mv - 700) / 900.0f; // assume 1.6V (100%) to 0.7V (0%) range @@ -788,7 +788,7 @@ static int alecto_ws1200v2_dcf_callback(r_device *decoder, bitbuffer_t *bitbuffe int time_m = b[8]; // (b[8] >> 4) * 10 + (b[8] & 0x0f); int time_s = b[9]; // (b[9] >> 4) * 10 + (b[9] & 0x0f); char clock_str[32]; - sprintf(clock_str, "%04x-%02x-%02xT%02x:%02x:%02x", + snprintf(clock_str, sizeof(clock_str), "%04x-%02x-%02xT%02x:%02x:%02x", date_y, date_m, date_d, time_h, time_m, time_s); /* clang-format off */ diff --git a/src/rtl_433/devices/fineoffset_wh1050.c b/src/rtl_433/devices/fineoffset_wh1050.c index 578fc16a..d740207c 100644 --- a/src/rtl_433/devices/fineoffset_wh1050.c +++ b/src/rtl_433/devices/fineoffset_wh1050.c @@ -1,8 +1,9 @@ /** @file - Fine Offset WH1050 Weather Station. + Fine Offset WH1050 and TFA 30.3151 Weather Station. 2016 Nicola Quiriti ('ovrheat') Modifications 2016 by Don More + 2023 Bruno OCTAU (ProfBoc75) for TFA 30.3151 FSK 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 @@ -10,8 +11,13 @@ (at your option) any later version. */ -/** -Fine Offset WH1050 Weather Station. + +#include "decoder.h" +#define TYPE_OOK 1 +#define TYPE_FSK 2 + +/** @fn in fineoffset_wh1050_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned bitpos, int type) +Fine Offset WH1050 and TFA 30.3151 Weather Station. This module is a cut-down version of the WH1080 decoder. The WH1050 sensor unit is like the WH1080 unit except it has no @@ -34,31 +40,166 @@ I.e., data packet, wait 48 seconds, two data packets, wait 48 seconds, data pack The 'Total rainfall' field is a cumulative counter, increased by 0.3 millimeters of rain each step. -Message layout and example: +The station is also known as TFA STRATOS 35.1077 +See the product page here: https://www.tfa-dostmann.de/en/product/wireless-weather-station-with-wind-and-rain-gauge-stratos-35-1077/ +This model seems also capable to decode the DCF77 time signal sent by the time signal decoder (which is enclosed on the sensor tx): +around the minute 59 of the even hours the sensor's TX stops sending weather data, probably to receive (and sync with) DCF77 signals. +After around 3-4 minutes of silence it starts to send just time data for some minute, then it starts again with +weather data as usual. + +TFA 30.3151 Sensor is FSK version and decodes here. See issue #2538: Preamble is aaaa2dd4 and Temperature is not offset and rain gauge is 0.5 mm by pulse. + +To recognize which message is received (weather or time) you can use the 'msg_type' field on json output: +- msg_type 5 = weather data +- msg_type 6 = time data + +Weather data - Message layout and example: - AA BC CD DD EE FF GG HH HH II - {80} ff 5f 51 93 48 00 00 12 46 aa + Preamble{8} : 0xFF - OOK Version + or Preamble{40} : 0xAAAAAA2DD4 - FSK Version -- A : 8 bits : Preamble 0xFF -- B : 4 bits : ?? - always seems to be 0x5 + Byte Position : 00 01 02 03 04 05 06 07 08 + Payload{72} : BC CD DD EE FF GG HH HH II + Sample{72} : 5f 51 93 48 00 00 12 46 aa + +- B : 4 bits : Msg Type - seems to be 0x5 for whether data, 0x6 for time data - C : 8 bits : Id, changes when reset (e.g., 0xF5) -- D : 1 bit : ?? - unknown, always seems to be 0 +- D : 1 bit : Temperature-Sign, only for FSK version - D : 1 bit : Battery, 0 = ok, 1 = low (e.g, OK) -- D : 10 bits : Temperature in Celsius, offset 400, scaled by 10 (e.g., 0.3 degrees C) +- D : 10 bits : Temperature in Celsius, [offset 400 only for OOK Version], scaled by 10 (e.g., 0.3 degrees C) - E : 8 bits : Relative humidity, percent (e.g., 72%) - F : 8 bits : Wind speed average in m/s, scaled by 1/0.34 (e.g., 0 m/s) - G : 8 bits : Wind speed gust in m/s, scaled by 1/0.34 (e.g., 0 m/s) -- H : 16 bits : Total rainfall in units of 0.3mm, since reset (e.g., 1403.4 mm) +- H : 16 bits : Total rainfall in units of 0.3mm (OOK version) or 0.5mm (FSK version), since reset (e.g., 1403.4 mm) - I : 8 bits : CRC, poly 0x31, init 0x00 (excluding preamble) -*/ +Time data - Message layout and example: -#include "decoder.h" + Preamble{8} : 0xFF - OOK Version + or Preamble{40} : 0xAAAAAA2DD4 - FSK Version -static int fineoffset_wh1050_callback(r_device *decoder, bitbuffer_t *bitbuffer) + Byte Position : 00 01 02 03 04 05 06 07 08 + Payload{72} : BC CD DE FG HI JK LM NO PP + Sample{72} : 69 0a 96 02 41 23 43 27 df + +- B : 4 bits : Msg Type - seems to be 0x5 for whether data, 0x6 for time data +- C : 8 bits : Id, changes when reset (e.g., 0x90) +- D : 1 bit : Unknown (always 1?) +- D : 1 bit : Battery, 0 = ok, 1 = low (e.g, OK) +- D : 4 bits : Unknown (always 0?) +- D : 2 bits : hour BCD coded (*10) +- E : 4 bits : hour BCD coded (*1) +- F : 4 bits : minute BCD coded (*10) +- G : 4 bits : minute BCD coded (*1) +- H : 4 bits : second BCD coded (*10) +- I : 4 bits : second BCD coded (*1) +- J : 4 bits : year BCD coded (*10), counted from 2000 +- K : 4 bits : year BCD coded (*1), counted from 2000 +- L : 3 bits : Unknown +- L : 1 bits : month BCD coded (*10) +- M : 4 bits : month BCD coded (*1) +- N : 4 bits : day BCD coded (*10) +- O : 4 bits : day BCD coded (*1) +- P : 8 bits : CRC, poly 0x31, init 0x00 (excluding preamble) + +*/ +static int fineoffset_wh1050_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned bitpos, int type) { data_t *data; uint8_t br[9]; + float temperature, rain; + + bitbuffer_extract_bytes(bitbuffer, 0, bitpos, br, 9 * 8); + + if (crc8(br, 9, 0x31, 0x00)) { + return 0; // DECODE_FAIL_MIC; + } + + // GETTING MESSAGE TYPE + int msg_type = (br[0] >> 4); + + if (msg_type == 5) { + // GETTING WEATHER SENSORS DATA + int temp_sign = (br[1] & 0x08) >> 3; // only FSK version + int temp_raw = ((br[1] & 0x03) << 8) | br[2]; + int rain_raw = (br[6] << 8) | br[7]; + if (type == TYPE_OOK) { + temperature = (temp_raw - 400) * 0.1f; + rain = rain_raw * 0.3f; + } + else { + temperature = temp_raw * 0.1f; + rain = rain_raw * 0.5f; + if (temp_sign) { + temperature = -temperature; + } + } + int humidity = br[3]; + float speed = (br[4] * 0.34f) * 3.6f; // m/s -> km/h + float gust = (br[5] * 0.34f) * 3.6f; // m/s -> km/h + int device_id = (br[0] << 4 & 0xf0) | (br[1] >> 4); + int battery_low = br[1] & 0x04; + + /* clang-format off */ + data = data_make( + "model", "", DATA_COND, type == TYPE_OOK, DATA_STRING, "Fineoffset-WH1050", + "model", "", DATA_COND, type == TYPE_FSK, DATA_STRING, "TFA-303151", + "id", "Station ID", DATA_FORMAT, "%02X", DATA_INT, device_id, + "msg_type", "Msg type", DATA_INT, msg_type, + "battery_ok", "Battery", DATA_INT, !battery_low, + "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temperature, + "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, + "wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.02f km/h", DATA_DOUBLE, speed, + "wind_max_km_h", "Wind gust", DATA_FORMAT, "%.02f km/h ", DATA_DOUBLE, gust, + "rain_mm", "Total rainfall", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain, + "mic", "Integrity", DATA_STRING, "CRC", + NULL); + /* clang-format on */ + } + else if (msg_type == 6) { + // GETTING TIME DATA + int device_id = (br[0] << 4 & 0xf0) | (br[1] >> 4); + int battery_low = br[1] & 0x04; + int hours = ((br[2] & 0x30) >> 4) * 10 + (br[2] & 0x0F); + int minutes = ((br[3] & 0xF0) >> 4) * 10 + (br[3] & 0x0F); + int seconds = ((br[4] & 0xF0) >> 4) * 10 + (br[4] & 0x0F); + int year = ((br[5] & 0xF0) >> 4) * 10 + (br[5] & 0x0F) + 2000; + int month = ((br[6] & 0x10) >> 4) * 10 + (br[6] & 0x0F); + int day = ((br[7] & 0xF0) >> 4) * 10 + (br[7] & 0x0F); + + char clock_str[23]; + snprintf(clock_str, sizeof(clock_str), "%04d-%02d-%02dT%02d:%02d:%02d", + year, month, day, hours, minutes, seconds); + + /* clang-format off */ + data = data_make( + "model", "", DATA_COND, type == TYPE_OOK, DATA_STRING, "Fineoffset-WH1050", + "model", "", DATA_COND, type == TYPE_FSK, DATA_STRING, "TFA-303151", + "id", "Station ID", DATA_FORMAT, "%02X", DATA_INT, device_id, + "msg_type", "Msg type", DATA_INT, msg_type, + "battery_ok", "Battery", DATA_INT, !battery_low, + "radio_clock", "Radio Clock", DATA_STRING, clock_str, + "mic", "Integrity", DATA_STRING, "CRC", + NULL); + /* clang-format on */ + } + else { + decoder_logf(decoder, 1, __func__, "Unknown msg type %x", msg_type); + return 0; // DECODE_FAIL_MIC; + } + + decoder_output_data(decoder, data); + return 1; +} + +/** +Fineoffset or TFA OOK/FSK protocol. +@sa fineoffset_wh1050_decode() +*/ +static int fineoffset_wh1050_callback(r_device *decoder, bitbuffer_t *bitbuffer) +{ + unsigned bitpos = 0; + int events = 0; if (bitbuffer->num_rows != 1) { return DECODE_ABORT_EARLY; @@ -73,62 +214,41 @@ static int fineoffset_wh1050_callback(r_device *decoder, bitbuffer_t *bitbuffer) 79 bit message. In both cases, we extract the 72 bits after the preamble. + + For FSK version TFA 30.3151 the preamble is aaaaaa2dd4 and message payload is 6 times repeats (gap, preamble, message, gap, ... ) in one row and 754 bits. + gap is 11 bits long, preamble need to be searched into a while loop to get the repeated message */ + unsigned bits = bitbuffer->bits_per_row[0]; - uint8_t preamble_byte = bitbuffer->bb[0][0]; + uint8_t preamble_byte = bitbuffer->bb[0][0]; // for OOK + uint8_t const preamble_fsk[] = {0xAA, 0x2D, 0xD4}; // part of preamble and sync word for FSK if (bits == 79 && preamble_byte == 0xfe) { - bitbuffer_extract_bytes(bitbuffer, 0, 7, br, 72); + fineoffset_wh1050_decode(decoder, bitbuffer, 7, TYPE_OOK); } else if (bits == 80 && preamble_byte == 0xff) { - bitbuffer_extract_bytes(bitbuffer, 0, 8, br, 72); + fineoffset_wh1050_decode(decoder, bitbuffer, 8, TYPE_OOK); + } else if (bits > 112 && bits < 760) { + while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, preamble_fsk, sizeof(preamble_fsk) * 8)) + 72 <= + bitbuffer->bits_per_row[0]) { + events += fineoffset_wh1050_decode(decoder, bitbuffer, bitpos + sizeof(preamble_fsk) * 8, TYPE_FSK); + bitpos += 123; + } } else { return DECODE_ABORT_LENGTH; } - - // If you calculate the CRC over all 10 bytes including the preamble - // byte (always 0xFF), then CRC_INIT is 0xFF. But we compare the preamble - // byte and then discard it. - if (crc8(br, 9, 0x31, 0x00)) { - return DECODE_FAIL_MIC; // crc mismatch - } - - // GETTING WEATHER SENSORS DATA - int temp_raw = ((br[1] & 0x03) << 8) | br[2]; - float temperature = (temp_raw - 400) * 0.1f; - int humidity = br[3]; - float speed = (br[4] * 0.34f) * 3.6f; // m/s -> km/h - float gust = (br[5] * 0.34f) * 3.6f; // m/s -> km/h - int rain_raw = (br[6] << 8) | br[7]; - float rain = rain_raw * 0.3f; - int device_id = (br[0] << 4 & 0xf0) | (br[1] >> 4); - int battery_low = br[1] & 0x04; - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Fineoffset-WH1050", - "id", "StationID", DATA_FORMAT, "%04X", DATA_INT, device_id, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temperature, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - "wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.02f", DATA_DOUBLE, speed, - "wind_max_km_h", "Wind gust", DATA_FORMAT, "%.02f", DATA_DOUBLE, gust, - "rain_mm", "Total rainfall", DATA_FORMAT, "%.01f", DATA_DOUBLE, rain, - "mic", "Integrity", DATA_STRING, "CRC", - NULL); - /* clang-format on */ - - decoder_output_data(decoder, data); - return 1; + return events; } static char const *const output_fields[] = { "model", "id", + "msg_type", "battery_ok", "temperature_C", "humidity", "wind_avg_km_h", "wind_max_km_h", "rain_mm", + "radio_clock", "mic", NULL, }; @@ -142,3 +262,13 @@ r_device const fineoffset_wh1050 = { .decode_fn = &fineoffset_wh1050_callback, .fields = output_fields, }; + +r_device const tfa_303151 = { + .name = "TFA 30.3151 Weather Station", + .modulation = FSK_PULSE_PCM, + .short_width = 60, + .long_width = 60, + .reset_limit = 2500, + .decode_fn = &fineoffset_wh1050_callback, + .fields = output_fields, +}; diff --git a/src/rtl_433/devices/fineoffset_wh1080.c b/src/rtl_433/devices/fineoffset_wh1080.c index a8e49831..a0f36b73 100644 --- a/src/rtl_433/devices/fineoffset_wh1080.c +++ b/src/rtl_433/devices/fineoffset_wh1080.c @@ -276,7 +276,7 @@ static int fineoffset_wh1080_callback(r_device *decoder, bitbuffer_t *bitbuffer, } else if (msg_type == 1) { char clock_str[23]; - sprintf(clock_str, "%04d-%02d-%02dT%02d:%02d:%02d", + snprintf(clock_str, sizeof(clock_str), "%04d-%02d-%02dT%02d:%02d:%02d", year, month, day, hours, minutes, seconds); /* clang-format off */ diff --git a/src/rtl_433/devices/fineoffset_ws90.c b/src/rtl_433/devices/fineoffset_ws90.c new file mode 100644 index 00000000..826efa8b --- /dev/null +++ b/src/rtl_433/devices/fineoffset_ws90.c @@ -0,0 +1,174 @@ +/** @file + Fine Offset Electronics WS90 weather station. + + Copyright (C) 2022 Christian W. Zuckschwerdt + Protocol description by @davidefa + + Copy of fineoffset_ws80.c with changes made to support Fine Offset WS90 + sensor array. Changes made by John Pochmara + + 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. +*/ + +#include "decoder.h" + +/** +Fine Offset Electronics WS90 weather station. + +The WS90 is a WS80 with the addition of a piezoelectric rain gauge. +Data bytes 1-13 are the same between the two models. The new rain data +is in bytes 16-20, with bytes 19 and 20 reporting total rain. Bytes +17 and 18 are affected by rain, but it is unknown what they report. Byte +21 reports the voltage of the super cap. And the checksum and CRC +have been moved to bytes 30 and 31. What is reported in the other +bytes is unknown at this time. + +Also sold by EcoWitt. + +Preamble is aaaa aaaa aaaa, sync word is 2dd4. + +Packet layout: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + YY II II II LL LL BB FF TT HH WW DD GG VV UU UU R0 R1 R2 R3 R4 SS UU UU UU UU UU UU UU ZZ AA XX + 90 00 34 2b 00 77 a4 82 62 39 00 3e 00 00 3f ff 20 00 ba 00 00 26 02 00 ff 9f f8 00 00 82 92 4f + +- Y = fixed sensor type 0x90 +- I = device ID, might be less than 24 bit? +- L = light value, unit of 10 lux +- B = battery voltage, unit of 20 mV, we assume a range of 3.0V to 1.4V +- F = flags and MSBs, 0x03: temp MSB, 0x10: wind MSB, 0x20: bearing MSB, 0x40: gust MSB + 0x80 or 0x08: maybe battery good? seems to be always 0x88 +- T = temperature, lowest 8 bits of temperature, offset 40, scale 10 +- H = humidity +- W = wind speed, lowest 8 bits of wind speed, m/s, scale 10 +- D = wind bearing, lowest 8 bits of wind bearing, range 0-359 deg, 0x1ff if invalid +- G = wind gust, lowest 8 bits of wind gust, m/s, scale 10 +- V = uv index, scale 10 +- U = unknown (bytes 14 and 15 appear to be fixed at 3f ff) +- R = rain total (R3 << 8 | R4) * 0.1 mm +- S = super cap voltage, unit of 0.1V, lower 6 bits, mask 0x3f +- Z = Firmware version. 0x82 = 130 = 1.3.0 +- A = checksum +- X = CRC + +*/ + +static int fineoffset_ws90_decode(r_device *decoder, bitbuffer_t *bitbuffer) +{ + uint8_t const preamble[] = {0xaa, 0xaa, 0x2d, 0xd4}; // 32 bit, part of preamble and sync word + uint8_t b[32]; + + // Validate package, WS90 nominal size is 345 bit periods + if (bitbuffer->bits_per_row[0] < 168 || bitbuffer->bits_per_row[0] > 350) { + decoder_logf_bitbuffer(decoder, 2, __func__, bitbuffer, "abort length" ); + return DECODE_ABORT_LENGTH; + } + + // Find a data package and extract data buffer + unsigned bit_offset = bitbuffer_search(bitbuffer, 0, 0, preamble, 32) + 32; + if (bit_offset + sizeof(b) * 8 > bitbuffer->bits_per_row[0]) { // Did not find a big enough package + decoder_logf_bitbuffer(decoder, 2, __func__, bitbuffer, "short package at %u (%u)", bit_offset, bitbuffer->bits_per_row[0]); + return DECODE_ABORT_LENGTH; + } + + // Extract package data + bitbuffer_extract_bytes(bitbuffer, 0, bit_offset, b, sizeof(b) * 8); + + if (b[0] != 0x90) // Check for family code 0x90 + return DECODE_ABORT_EARLY; + + decoder_logf(decoder, 1, __func__, "WS90 detected, buffer is %u bits length", bitbuffer->bits_per_row[0]); + + // Verify checksum and CRC + uint8_t crc = crc8(b, 31, 0x31, 0x00); + uint8_t chk = add_bytes(b, 31); + if (crc != 0 || chk != b[31]) { + decoder_logf(decoder, 1, __func__, "Checksum error: %02x %02x (%02x)", crc, chk, b[31]); + return DECODE_FAIL_MIC; + } + + int id = (b[1] << 16) | (b[2] << 8) | (b[3]); + int light_raw = (b[4] << 8) | (b[5]); + float light_lux = light_raw * 10; // Lux + //float light_wm2 = light_raw * 0.078925f; // W/m2 + int battery_mv = (b[6] * 20); // mV + int battery_lvl = battery_mv < 1400 ? 0 : (battery_mv - 1400) / 16; // 1.4V-3.0V is 0-100 + int flags = b[7]; // to find the wind msb + int temp_raw = ((b[7] & 0x03) << 8) | (b[8]); + float temp_c = (temp_raw - 400) * 0.1f; + int humidity = (b[9]); + int wind_avg = ((b[7] & 0x10) << 4) | (b[10]); + int wind_dir = ((b[7] & 0x20) << 3) | (b[11]); + int wind_max = ((b[7] & 0x40) << 2) | (b[12]); + int uv_index = (b[13]); + int rain_raw = (b[19] << 8 ) | (b[20]); + int supercap_V = (b[21] & 0x3f); + int firmware = b[29]; + + if (battery_lvl > 100) // More then 100%? + battery_lvl = 100; + + char extra[31]; + snprintf(extra, sizeof(extra), "%02x%02x%02x%02x%02x------%02x%02x%02x%02x%02x%02x%02x", b[14], b[15], b[16], b[17], b[18], /* b[19,20] is the rain sensor, b[21] is supercap_V */ b[22], b[23], b[24], b[25], b[26], b[27], b[28]); + + /* clang-format off */ + data_t *data = data_make( + "model", "", DATA_STRING, "Fineoffset-WS90", + "id", "ID", DATA_FORMAT, "%06x", DATA_INT, id, + "battery_ok", "Battery", DATA_DOUBLE, battery_lvl * 0.01f, + "battery_mV", "Battery Voltage", DATA_FORMAT, "%d mV", DATA_INT, battery_mv, + "temperature_C", "Temperature", DATA_COND, temp_raw != 0x3ff, DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, + "humidity", "Humidity", DATA_COND, humidity != 0xff, DATA_FORMAT, "%u %%", DATA_INT, humidity, + "wind_dir_deg", "Wind direction", DATA_COND, wind_dir != 0x1ff, DATA_INT, wind_dir, + "wind_avg_m_s", "Wind speed", DATA_COND, wind_avg != 0x1ff, DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wind_avg * 0.1f, + "wind_max_m_s", "Gust speed", DATA_COND, wind_max != 0x1ff, DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wind_max * 0.1f, + "uvi", "UVI", DATA_COND, uv_index != 0xff, DATA_FORMAT, "%.1f", DATA_DOUBLE, uv_index * 0.1f, + "light_lux", "Light", DATA_COND, light_raw != 0xffff, DATA_FORMAT, "%.1f lux", DATA_DOUBLE, (double)light_lux, + "flags", "Flags", DATA_FORMAT, "%02x", DATA_INT, flags, + "rain_mm", "Total Rain", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_raw * 0.1f, + "supercap_V", "Supercap Voltage", DATA_COND, supercap_V != 0xff, DATA_FORMAT, "%.1f V", DATA_DOUBLE, supercap_V * 0.1f, + "firmware", "Firmware Version", DATA_INT, firmware, + "data", "Extra Data", DATA_STRING, extra, + "mic", "Integrity", DATA_STRING, "CRC", + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; +} + +static char const *const output_fields[] = { + "model", + "id", + "battery_ok", + "battery_mV", + "temperature_C", + "humidity", + "wind_dir_deg", + "wind_avg_m_s", + "wind_max_m_s", + "uvi", + "light_lux", + "flags", + "unknown", + "rain_mm", + "supercap_V", + "firmware", + "data", + "mic", + NULL, +}; + +r_device const fineoffset_ws90 = { + .name = "Fine Offset Electronics WS90 weather station", + .modulation = FSK_PULSE_PCM, + .short_width = 58, + .long_width = 58, + .reset_limit = 3000, + .decode_fn = &fineoffset_ws90_decode, + .fields = output_fields, +}; diff --git a/src/rtl_433/devices/flex.c b/src/rtl_433/devices/flex.c index 52790151..a2c518bf 100644 --- a/src/rtl_433/devices/flex.c +++ b/src/rtl_433/devices/flex.c @@ -317,6 +317,11 @@ static int flex_callback(r_device *decoder, bitbuffer_t *bitbuffer) // add a data line for each getter render_getters(row_data[i], bitbuffer->bb[i], params); + // print at least one '0' + if (row_bytes[0] == '\0') { + snprintf(row_bytes, sizeof(row_bytes), "0"); + } + // a simpler representation for csv output row_codes[i] = malloc(8 + bitbuffer->bits_per_row[i] / 4 + 1); // "{nnnn}..\0" if (!row_codes[i]) diff --git a/src/rtl_433/devices/flowis.c b/src/rtl_433/devices/flowis.c index 34764dd9..09047856 100644 --- a/src/rtl_433/devices/flowis.c +++ b/src/rtl_433/devices/flowis.c @@ -53,8 +53,6 @@ static int flowis_decode(r_device *decoder, bitbuffer_t *bitbuffer) 0xd3, 0x91, 0xd3, 0x91 // sync word }; - data_t *data; - if (bitbuffer->num_rows != 1) { return DECODE_ABORT_EARLY; } @@ -71,7 +69,7 @@ static int flowis_decode(r_device *decoder, bitbuffer_t *bitbuffer) bitbuffer_extract_bytes(bitbuffer, row, start_pos + sizeof (preamble) * 8, &len, 8); - uint8_t frame[256+2+1] = {0}; // uint8_t max bytes + 2 bytes crc + 1 lenght byte + uint8_t frame[256+2+1] = {0}; // uint8_t max bytes + 2 bytes crc + 1 length byte frame[0] = len; // Get frame (len don't include the length byte and the crc16 bytes) bitbuffer_extract_bytes(bitbuffer, row, @@ -96,26 +94,26 @@ static int flowis_decode(r_device *decoder, bitbuffer_t *bitbuffer) int id = b[5] << 24 | b[4] << 16 | b[3] << 8 | b[2]; int volume = b[13] << 16 | b[12] << 8 | b[11]; - char fts_str[20]; int fts_year = b[10] >> 2; int fts_mth = ((b[9]>>6) | (b[10]&3)<<2); int fts_day = (b[9]&0x3E) >> 1; int fts_hour = (b[8]>>4) | ((b[9]&1)<<4); int fts_min = ((b[8]&0xF)<<2) | ((b[7]&0xC0)>>6); int fts_sec = b[7]&0x3F; - sprintf(fts_str, "%4d-%02d-%02dT%02d:%02d:%02d", fts_year + 2000, fts_mth, fts_day, fts_hour, fts_min, fts_sec); + char fts_str[20]; + snprintf(fts_str, sizeof(fts_str), "%4d-%02d-%02dT%02d:%02d:%02d", fts_year + 2000, fts_mth, fts_day, fts_hour, fts_min, fts_sec); /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Flowis", - "id", "Meter id", DATA_INT, id, - "type", "Type", DATA_INT, type, - "volume_m3", "Volume", DATA_FORMAT, "%.3f m3", DATA_DOUBLE, volume/1000.0, - "device_time", "Device time", DATA_STRING, fts_str, - "alarm", "Alarm", DATA_INT, b[15], - "backflow", "Backflow", DATA_INT, b[14], - "mic", "Integrity", DATA_STRING, "CRC", - NULL); + data_t *data = data_make( + "model", "", DATA_STRING, "Flowis", + "id", "Meter id", DATA_INT, id, + "type", "Type", DATA_INT, type, + "volume_m3", "Volume", DATA_FORMAT, "%.3f m3", DATA_DOUBLE, volume/1000.0, + "device_time", "Device time", DATA_STRING, fts_str, + "alarm", "Alarm", DATA_INT, b[15], + "backflow", "Backflow", DATA_INT, b[14], + "mic", "Integrity", DATA_STRING, "CRC", + NULL); /* clang-format on */ decoder_output_data(decoder, data); diff --git a/src/rtl_433/devices/funkbus.c b/src/rtl_433/devices/funkbus.c index 464a58b4..75833342 100644 --- a/src/rtl_433/devices/funkbus.c +++ b/src/rtl_433/devices/funkbus.c @@ -74,12 +74,12 @@ static uint8_t calc_checksum(uint8_t const *bitrow, unsigned len) const uint8_t full_bytes = len / 8; const uint8_t bits_left = len % 8; - uint8_t xor = xor_bytes(bitrow, full_bytes); + uint8_t xor_byte = xor_bytes(bitrow, full_bytes); if (bits_left) { - xor ^= bitrow[full_bytes] & ~BIT_MASK(8 - bits_left); + xor_byte ^= bitrow[full_bytes] & ~BIT_MASK(8 - bits_left); } - const uint8_t xor_nibble = ((xor&0xF0) >> 4) ^ (xor&0x0F); + const uint8_t xor_nibble = ((xor_byte&0xF0) >> 4) ^ (xor_byte&0x0F); uint8_t result = 0; if (xor_nibble & 0x8) { @@ -96,7 +96,7 @@ static uint8_t calc_checksum(uint8_t const *bitrow, unsigned len) } result = result & 0xF; - result |= (parity8(xor) << 4); + result |= (parity8(xor_byte) << 4); return result; } diff --git a/src/rtl_433/devices/generic_motion.c b/src/rtl_433/devices/generic_motion.c index d1ce8588..4eec1462 100644 --- a/src/rtl_433/devices/generic_motion.c +++ b/src/rtl_433/devices/generic_motion.c @@ -16,10 +16,10 @@ Example codes are: 80042 Arm alarm, 80002 Disarm alarm, (following motion detection the sensor will blackout for 90 seconds). 2315 baud on/off rate and alternating 579 baud bit rate and 463 baud bit rate -Each transmission has a warmup of 17 to 32 pulse widths then 8 packets with +Each transmission has a warm-up of 17 to 32 pulse widths then 8 packets with alternating 1:3 / 2:2 or 1:4 / 2:3 gap:pulse ratio for 0/1 bit in the packet with a repeat gap of 4 pulse widths, i.e.: -- 6704 us to 13092 us warmup pulse, 1672 us gap, +- 6704 us to 13092 us warm-up pulse, 1672 us gap, - 0: 472 us gap, 1332 us pulse - 1: 920 us gap, 888 us pulse - 1672 us repeat gap, @@ -32,13 +32,8 @@ with a repeat gap of 4 pulse widths, i.e.: static int generic_motion_callback(r_device *decoder, bitbuffer_t *bitbuffer) { - data_t *data; - uint8_t *b; - int code; - char code_str[6]; - for (int i = 0; i < bitbuffer->num_rows; ++i) { - b = bitbuffer->bb[i]; + uint8_t *b = bitbuffer->bb[i]; // strictly validate package as there is no checksum if ((bitbuffer->bits_per_row[i] != 20) || ((b[1] == 0) && (b[2] == 0)) @@ -46,11 +41,12 @@ static int generic_motion_callback(r_device *decoder, bitbuffer_t *bitbuffer) || bitbuffer_count_repeats(bitbuffer, i, 0) < 3) continue; // DECODE_ABORT_EARLY - code = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); - sprintf(code_str, "%05x", code); + int code = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); + char code_str[6]; + snprintf(code_str, sizeof(code_str), "%05x", code); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Generic-Motion", "code", "", DATA_STRING, code_str, NULL); diff --git a/src/rtl_433/devices/govee.c b/src/rtl_433/devices/govee.c index e8b1d817..7a42a0aa 100644 --- a/src/rtl_433/devices/govee.c +++ b/src/rtl_433/devices/govee.c @@ -156,7 +156,7 @@ static int govee_decode(r_device *decoder, bitbuffer_t *bitbuffer) // dump raw input code char code_str[13]; - sprintf(code_str, "%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5]); + snprintf(code_str, sizeof(code_str), "%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5]); bitbuffer_invert(bitbuffer); @@ -198,12 +198,21 @@ static int govee_decode(r_device *decoder, bitbuffer_t *bitbuffer) event &= 0x0FFF; char const *event_str; + int wet = -1; // Figure out what event was triggered if (event == 0xafa) { event_str = "Button Press"; + // The H5054 water sensor does not send a message when it transitions from wet to dry nor does it have a + // dedicated message to indicate that it is not wet. However, the sensor only sends a "button press" message if + // the button is pressed while the device is dry (no button press message is sent if the button is pressed while + // the sensor is wet). Since we know the sensor is dry when a "button press" message is received, "detect_wet:0" + // is included in the output when the button is pressed as a workaround to allow the user to transition the + // device back to the dry state. + wet = 0; } else if (event == 0xbfb) { event_str = "Water Leak"; + wet = 1; } else if (event_type == 0xc) { event_str = "Battery Report"; @@ -227,6 +236,7 @@ static int govee_decode(r_device *decoder, bitbuffer_t *bitbuffer) "id" , "", DATA_INT, id, "battery_ok", "Battery level", DATA_COND, battery, DATA_DOUBLE, battery_level, "battery_mV", "Battery", DATA_COND, battery, DATA_FORMAT, "%d mV", DATA_INT, battery_mv, + "detect_wet", "", DATA_COND, wet >= 0, DATA_INT, wet, "event", "", DATA_STRING, event_str, "code", "Raw Code", DATA_STRING, code_str, "mic", "Integrity", DATA_STRING, "PARITY", @@ -243,6 +253,7 @@ static char const *const output_fields[] = { "id", "battery_ok", "battery_mV", + "detect_wet", "event", "code", "mic", @@ -308,12 +319,6 @@ device. */ -typedef enum { - GOVEE_BUTTON_PRESS = 0, - GOVEE_BATTERY_REPORT = 1, - GOVEE_WATER_LEAK = 2, -} govee_h5054_event_t; - static int govee_h5054_decode(r_device *decoder, bitbuffer_t *bitbuffer) { if (bitbuffer->num_rows < 3) { @@ -334,7 +339,7 @@ static int govee_h5054_decode(r_device *decoder, bitbuffer_t *bitbuffer) uint8_t *b = bitbuffer->bb[r]; char code_str[13]; - sprintf(code_str, "%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5]); + snprintf(code_str, sizeof(code_str), "%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5]); uint16_t chk = crc16(b, 6, 0x1021, 0x1d0f); if (chk != 0) { @@ -355,18 +360,27 @@ static int govee_h5054_decode(r_device *decoder, bitbuffer_t *bitbuffer) decoder_logf(decoder, 1, __func__, "crc_sum=%04x", crc_sum); char const *event_str; + int wet = -1; int leak_num = -1; int battery = -1; switch (event) { - case GOVEE_BUTTON_PRESS: + case 0x0: event_str = "Button Press"; + // The H5054 water sensor does not send a message when it transitions from wet to dry nor does it have a + // dedicated message to indicate that it is not wet. However, the sensor only sends a "button press" message if + // the button is pressed while the device is dry (no button press message is sent if the button is pressed while + // the sensor is wet). Since we know the sensor is dry when a "button press" message is received, "detect_wet:0" + // is included in the output when the button is pressed as a workaround to allow the user to transition the + // device back to the dry state. + wet = 0; break; - case GOVEE_BATTERY_REPORT: + case 0x1: event_str = "Battery Report"; battery = event_data; break; - case GOVEE_WATER_LEAK: + case 0x2: event_str = "Water Leak"; + wet = 1; leak_num = event_data; break; default: @@ -384,6 +398,7 @@ static int govee_h5054_decode(r_device *decoder, bitbuffer_t *bitbuffer) "battery_ok", "Battery level", DATA_COND, battery >= 0, DATA_DOUBLE, battery_level, "battery_mV", "Battery", DATA_COND, battery >= 0, DATA_FORMAT, "%d mV", DATA_INT, battery_mv, "event", "", DATA_STRING, event_str, + "detect_wet", "", DATA_COND, wet >= 0, DATA_INT, wet, "leak_num", "Leak Num", DATA_COND, leak_num >= 0, DATA_INT, leak_num, "code", "Raw Code", DATA_STRING, code_str, "mic", "Integrity", DATA_STRING, "CRC", diff --git a/src/rtl_433/devices/hcs200.c b/src/rtl_433/devices/hcs200.c index 22d338f1..fbb81b86 100644 --- a/src/rtl_433/devices/hcs200.c +++ b/src/rtl_433/devices/hcs200.c @@ -25,7 +25,7 @@ Hardware buttons might map to combinations of these bits. - Datasheet HCS200: http://ww1.microchip.com/downloads/en/devicedoc/40138c.pdf - Datasheet HCS300: http://ww1.microchip.com/downloads/en/devicedoc/21137g.pdf -The warmup of 12 short pulses is followed by a long 4400 us gap. +The warm-up of 12 short pulses is followed by a long 4400 us gap. There are two packets with a 17500 us gap. rtl_433 -R 0 -X 'n=hcs200,m=OOK_PWM,s=370,l=772,r=9000,g=1500,t=152' @@ -66,9 +66,9 @@ static int hcs200_callback(r_device *decoder, bitbuffer_t *bitbuffer) int repeat = (b[8] & 0x40) == 0x40; char encrypted_str[9]; - sprintf(encrypted_str, "%08X", encrypted); + snprintf(encrypted_str, sizeof(encrypted_str), "%08X", encrypted); char serial_str[9]; - sprintf(serial_str, "%07X", serial); + snprintf(serial_str, sizeof(serial_str), "%07X", serial); /* clang-format off */ data_t *data = data_make( diff --git a/src/rtl_433/devices/holman_ws5029.c b/src/rtl_433/devices/holman_ws5029.c index fc65c9df..4ddf287e 100644 --- a/src/rtl_433/devices/holman_ws5029.c +++ b/src/rtl_433/devices/holman_ws5029.c @@ -1,6 +1,8 @@ /** @file - Decoder for Holman Industries WS5029 weather station. + AOK Electronic Limited weather station. + Copyright (C) 2023 Bruno OCTAU (ProfBoc75) (improve integrity check for all devices here and add support for AOK-5056 weather station PR #2419) + Copyright (C) 2023 Christian W. Zuckschwerdt ( reverse galois and xor_shift_bytes check algorithms PR #2419) Copyright (C) 2019 Ryan Mounce (PCM version) Copyright (C) 2018 Brad Campbell (PWM version) @@ -9,31 +11,38 @@ the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -/** -Decoder for Holman Industries WS5029 weather station, -a.k.a. Holman iWeather Station. -https://www.holmanindustries.com.au/products/iweather-station/ +/** @fn int holman_ws5029pcm_decode(r_device *decoder, bitbuffer_t *bitbuffer) +AOK Electronic Limited weather station. -Appears to be related to the Fine Offset WH1080 and Digitech XC0348. +Known Rebrand compatible with: +- Holman iWeather Station ws5029. https://www.holmanindustries.com.au/products/iweather-station/ +- Conrad Renkforce AOK-5056 +- Optex Electronique 990018 SM-018 5056 + +Appears to be related to the Fine pos WH1080 and Digitech XC0348. - Modulation: FSK PCM - Frequency: 917.0 MHz +- 40 kHz - 10 kb/s bitrate, 100 us symbol/bit time A transmission burst is sent every 57 seconds. Each burst consists of 3 -repititions of the same 192 bit "package" separated by a 1 ms gap. +repetitions of the same "package" separated by a 1 ms gap. +The length of 196 or 218 bits depends on the device type. Package format: - Preamble {48}0xAAAAAAAAAAAA - Header {24}0x98F3A5 -- Payload {96} see below -- Checksum {8} unidentified -- Trailer/Postamble {16} ??? +- Payload {96 or 146} see below +- zeros {36} 0 with battery ? +- Checksum/CRC {8} xor 12 bytes then reverse Galois algorithm (gen = 0x00, key = 0x31) PR #2419 +- Trailer/postamble {20} direction (previous ?) and 3 zeros -Payload format: +Payload format: Without UV Lux sensor + + Fixed Values 0x : AA AA AA AA AA AA 98 F3 A5 - Byte (dec) 09 10 11 12 13 14 15 16 17 18 19 20 - Nibble key II II CC CH HR RR WW Dx xx xx xx xx + Byte position : 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 + Payload : II II CC CH HR RR WW Dx xx xx ?x xx ss 0d 00 0 - IIII station ID (randomised on each battery insertion) - CCC degrees C, signed, in multiples of 0.1 C @@ -42,9 +51,35 @@ Payload format: - WW wind speed in km/h - D wind direction (0 = N, 4 = E, 8 = S, 12 = W) - xxxxxxxxx ???, usually zero +- ss xor 12 bytes then reverse Galois algorithm (gen = 0x00 , key = 0x31) PR #2419 + +Payload format: With UV Lux sensor + + Fixed Values 0x : AA AA AA AA AA AA 98 F3 A5 + + Byte position : 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 + Payload : II II CC CH HR RR WW | | NN SS 0D 00 00 00 00 0 + +-----------+ +-------------+ + | | + | 07 08 09 10 | + bits details : DDDDUUUU ULLLLLLL LLLLLLLL LLBBNNNN + +- I station ID (randomised on each battery insertion) +- C degrees C, signed, in multiples of 0.1 C +- H humidity % +- R cumulative rain in mm +- W wind speed in km/h +- D wind direction (0 = N, 4 = E, 8 = S, 12 = W) +- U Index UV +- L Lux +- B Battery +- N Payload number, increase at each message 000->FFF but not always, strange behavior. no clue +- S xor 12 bytes then reverse Galois algorithm (gen = 0x00 , key = 0x31) PR #2419 +- D Previous Wind direction +- Fixed values to 9 zeros To get raw data -$ rtl_433 -f 917M -X 'name=WS5029,modulation=FSK_PCM,short=100,long=100,preamble={48}0xAAAAAAAAAAAA,reset=19200' +$ rtl_433 -f 917M -X 'name=AOK,modulation=FSK_PCM,short=100,long=100,preamble={48}0xAAAAAA98F3A5,reset=22000' @sa holman_ws5029pwm_decode() @@ -55,67 +90,99 @@ To get raw data static int holman_ws5029pcm_decode(r_device *decoder, bitbuffer_t *bitbuffer) { int const wind_dir_degr[] = {0, 23, 45, 68, 90, 113, 135, 158, 180, 203, 225, 248, 270, 293, 315, 338}; - uint8_t const preamble[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x98, 0xF3, 0xA5}; + uint8_t const preamble[] = {0xAA, 0xAA, 0xAA, 0x98, 0xF3, 0xA5}; data_t *data; - uint8_t b[24]; + uint8_t b[18]; if (bitbuffer->num_rows != 1) { + if (decoder->verbose) { + decoder_logf(decoder, 1, __func__, "Wrong number of rows (%d)", bitbuffer->num_rows); + } return DECODE_ABORT_EARLY; } unsigned bits = bitbuffer->bits_per_row[0]; - // FSK sometimes decodes an extra bit at the start - // and likely extra 2-4 bits at the end - // let's allow for the leading bit and the whole gap period - if (bits < 192 || bits > 203) { + if (bits < 192 ) { // too small return DECODE_ABORT_LENGTH; } - unsigned offset = bitbuffer_search(bitbuffer, 0, 0, preamble, sizeof (preamble) * 8); - if (offset + 192 > bitbuffer->bits_per_row[0]) { + unsigned pos = bitbuffer_search(bitbuffer, 0, 0, preamble, sizeof (preamble) * 8); + + if (pos >= bits) { return DECODE_ABORT_EARLY; } - bitbuffer_extract_bytes(bitbuffer, 0, offset, b, 192); - - // byte 21 looks like a checksum - no success with brute force - /* - for (uint8_t firstbyte = 0; firstbyte < 21; firstbyte++) { - for (uint8_t poly=0; poly<255; poly++) { - if (crc8(&b[firstbyte], 21-firstbyte, poly, 0x00) == b[21]) { - decoder_logf(decoder, 3, __func__, "CORRECT CRC8 with offset %u poly 0x%x", firstbyte, poly); - } - if (crc8le(&b[firstbyte], 21-firstbyte, poly, 0x00) == b[21]) { - decoder_logf(decoder, 3, __func__, "CORRECT CRC8LE with offset %u poly 0x%x", firstbyte, poly); - } - } - } - */ - int device_id = (b[9] << 8) | b[10]; - int temp_raw = (int16_t)((b[11] << 8) | (b[12] & 0xf0)); // uses sign-extend - float temp_c = (temp_raw >> 4) * 0.1f; - int humidity = ((b[12] & 0x0f) << 4) | ((b[13] & 0xf0) >> 4); - int rain_raw = ((b[13] & 0x0f) << 8) | b[14]; - float rain_mm = rain_raw * 0.79f; - int speed_kmh = b[15]; - int direction_deg = wind_dir_degr[(b[16] & 0xf0) >> 4]; + decoder_logf(decoder, 2, __func__, "Found AOK preamble pos: %d", pos); - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Holman-WS5029", - "id", "StationID", DATA_FORMAT, "%04X", DATA_INT, device_id, - "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - "rain_mm", "Total rainfall", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain_mm, - "wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%u km/h", DATA_INT, speed_kmh, - "wind_dir_deg", "Wind Direction", DATA_INT, direction_deg, - NULL); - /* clang-format on */ + pos += sizeof(preamble) * 8; - decoder_output_data(decoder, data); - return 1; + bitbuffer_extract_bytes(bitbuffer, 0, pos, b, sizeof(b) * 8); + + uint8_t chk_digest = b[12]; + uint8_t chk_calc = xor_bytes(b, 12); + // reverse Galois algorithm then (gen = 0x00, key = 0x31) PR #2419 + int chk_expected = lfsr_digest8_reflect(&chk_calc, 1, 0x00, 0x31); + + if (chk_expected != chk_digest) { + return DECODE_FAIL_MIC; + } + + int device_id = (b[0] << 8) | b[1]; + int temp_raw = (int16_t)((b[2] << 8) | (b[3] & 0xf0)); // uses sign-extend + float temp_c = (temp_raw >> 4) * 0.1f; + int humidity = ((b[3] & 0x0f) << 4) | ((b[4] & 0xf0) >> 4); + int rain_raw = ((b[4] & 0x0f) << 8) | b[5]; + float speed_kmh = (float)b[6]; + int direction_deg = wind_dir_degr[(b[7] & 0xf0) >> 4]; + + if (bits < 200) { // model without UV LUX + float rain_mm = rain_raw * 0.79f; + + /* clang-format off */ + data = data_make( + "model", "", DATA_STRING, "Holman-WS5029", + "id", "Station ID", DATA_FORMAT, "%04X", DATA_INT, device_id, + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, + "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, + "rain_mm", "Total rainfall", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, + "wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh, + "wind_dir_deg", "Wind Direction", DATA_INT, direction_deg, + "mic", "Integrity", DATA_STRING, "CHECKSUM", + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; + } + else if (bits < 221) { // model with UV LUX + float rain_mm = rain_raw * 1.0f; + int uv_index = ((b[7] & 0x07) << 1) | ((b[8] & 0x80) >> 7); + int light_lux = ((b[8] & 0x7F) << 10) | (b[9] << 2) | ((b[10] & 0xC0) >> 6); + int battery_low = ((b[10] & 0x30) >> 4); + int counter = ((b[10] & 0x0f) << 8 | b[11]); + /* clang-format off */ + data = data_make( + "model", "", DATA_STRING, "AOK-5056", + "id", "Station ID", DATA_FORMAT, "%04X", DATA_INT, device_id, + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, + "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, + "rain_mm", "Total rainfall", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, + "wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh, + "wind_dir_deg", "Wind Direction", DATA_INT, direction_deg, + "uv", "UV Index", DATA_FORMAT, "%u", DATA_INT, uv_index, + "light_lux", "Lux", DATA_FORMAT, "%u", DATA_INT, light_lux, + "counter", "Counter", DATA_FORMAT, "%u", DATA_INT, counter, + "battery_ok", "battery", DATA_FORMAT, "%u", DATA_INT, !battery_low, + "mic", "Integrity", DATA_STRING, "CHECKSUM", + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; + } + return 0; } static char const *const output_fields[] = { @@ -127,12 +194,15 @@ static char const *const output_fields[] = { "rain_mm", "wind_avg_km_h", "wind_dir_deg", + "uv", + "light_lux", + "counter", "mic", NULL, }; r_device const holman_ws5029pcm = { - .name = "Holman Industries iWeather WS5029 weather station (newer PCM)", + .name = "AOK Weather Station rebrand Holman Industries iWeather WS5029, Conrad AOK-5056, Optex 990018", .modulation = FSK_PULSE_PCM, .short_width = 100, .long_width = 100, @@ -141,20 +211,52 @@ r_device const holman_ws5029pcm = { .fields = output_fields, }; -/** +/** @fn int holman_ws5029pwm_decode(r_device *decoder, bitbuffer_t *bitbuffer) Holman Industries WS5029 weather station using PWM. -- The checksum used is an xor of all 11 bytes. -- The bottom nybble results in 0. The top does not -- and I've been unable to figure out why. We only -- check the bottom nybble therefore. -- Have tried all permutations of init/poly for lfsr8 & crc8 -- Rain is 0.79mm / count - 618 counts / 488.2mm - 190113 - Multiplier is exactly 0.79 -- Wind is discrete kph -- Preamble is 0xaa 0xa5. Device is 0x98 +Package format: (invert) +- Preamble {24} 0xAAA598 +- Payload {56} [ see below ] +- Checksum/CRC {8} xor_shift_bytes (key = 0x18) PR #2419 +- Trailer/postamble {8} 0x00 or 0x80 + +Payload format: + + Byte position : 00 01 02[03 04 05 06 07 08 09]10 11 + Payload : AA A5 98 II BC CC HH RR RW WD SS 00 + +- I station ID +- B battery low indicator +- C degrees C, signed, in multiples of 0.1 C +- H Humidity 0-100 % +- R Rain is 0.79mm / count , 618 counts / 488.2mm - 190113 - Multiplier is exactly 0.79 +- W Wind speed in km/h +- D Wind direction, clockwise from North, in multiples of 22.5 deg +- S xor_shift_bytes , see PR #2419 + +To get the raw data : +$ rtl_433 -f 433.92M -X "n=Holman-WS5029-PWM,m=FSK_PWM,s=488,l=976,g=2000,r=6000,invert" */ + +static uint8_t xor_shift_bytes(uint8_t const message[], unsigned num_bytes, uint8_t shift_up) // see #2419 for more details about the xor_shift_bytes , used by PWM device +{ + uint8_t result0 = 0; + for (unsigned i = 0; i < num_bytes; i += 2) { + result0 ^= message[i]; + } + uint8_t result1 = 0; + for (unsigned i = 1; i < num_bytes; i += 2) { + result1 ^= message[i]; + } + uint8_t resultx = 0; + for (unsigned j = 0; j < 7; ++j) { + if (shift_up & (1 << j)) + resultx ^= result0 << (j + 1); + } + return result0 ^ result1 ^ resultx; +} + static int holman_ws5029pwm_decode(r_device *decoder, bitbuffer_t *bitbuffer) { uint8_t const preamble[] = {0x55, 0x5a, 0x67}; // Preamble/Device inverted @@ -162,8 +264,8 @@ static int holman_ws5029pwm_decode(r_device *decoder, bitbuffer_t *bitbuffer) data_t *data; uint8_t *b; uint16_t temp_raw; - int id, humidity, speed_kmh, wind_dir, battery_low; - float temp_c, rain_mm; + int id, humidity, wind_dir, battery_low; + float temp_c, rain_mm, speed_kmh; // Data is inverted, but all these checks can be performed // and validated prior to inverting the buffer. Invert @@ -178,20 +280,25 @@ static int holman_ws5029pwm_decode(r_device *decoder, bitbuffer_t *bitbuffer) if (memcmp(b, preamble, 3)) return DECODE_FAIL_SANITY; - // Test Checksum. - if ((xor_bytes(b, 11) & 0xF) ^ 0xF) - return DECODE_FAIL_MIC; - // Invert data for processing bitbuffer_invert(bitbuffer); + uint8_t chk_digest = b[10]; + // xor_shift_bytes , see PR #2419 + int chk_calc = xor_shift_bytes(b, 10, 0x18); + //fprintf(stderr, "%s: 11th byte %02x chk_calc %02x \n", __func__, chk_digest, chk_calc ); + + if (chk_calc != chk_digest) { + return DECODE_FAIL_MIC; + } + id = b[3]; // changes on each power cycle battery_low = (b[4] & 0x80); // High bit is low battery indicator temp_raw = (int16_t)(((b[4] & 0x0f) << 12) | (b[5] << 4)); // uses sign-extend temp_c = (temp_raw >> 4) * 0.1f; // Convert sign extended int to float humidity = b[6]; // Simple 0-100 RH - rain_mm = ((b[7] << 4) + (b[8] >> 4)) * 0.79f; // Multiplier tested empirically over 618 pulses - speed_kmh = ((b[8] & 0xF) << 4) + (b[9] >> 4); // In discrete kph + rain_mm = ((b[7] << 4) + (b[8] >> 4)) * 0.79f; // Multiplier tested empirically over 618 pulses + speed_kmh = (float)(((b[8] & 0xF) << 4) + (b[9] >> 4)); // In discrete kph wind_dir = b[9] & 0xF; // 4 bit wind direction, clockwise from North /* clang-format off */ @@ -199,10 +306,10 @@ static int holman_ws5029pwm_decode(r_device *decoder, bitbuffer_t *bitbuffer) "model", "", DATA_STRING, "Holman-WS5029", "id", "", DATA_INT, id, "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - "rain_mm", "Total rainfall", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain_mm, - "wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%u km/h", DATA_INT, speed_kmh, + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, + "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, + "rain_mm", "Total rainfall", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, + "wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh, "wind_dir_deg", "Wind Direction", DATA_INT, (int)(wind_dir * 22.5), "mic", "Integrity", DATA_STRING, "CHECKSUM", NULL); diff --git a/src/rtl_433/devices/honeywell_cm921.c b/src/rtl_433/devices/honeywell_cm921.c index bbcce1b4..4adf02c5 100644 --- a/src/rtl_433/devices/honeywell_cm921.c +++ b/src/rtl_433/devices/honeywell_cm921.c @@ -75,7 +75,7 @@ static const dev_map_entry_t device_map[] = { {.t = 10, .s = "OTB"}, // OpenTherm bridge (R8810) {.t = 12, .s = "THm"}, // Thermostat with setpoint schedule control (DTS92E, CME921) {.t = 13, .s = "BDR"}, // Wireless relay box (BDR91) (HC60NG too?) - {.t = 17, .s = " 17"}, // Dunno - Outside weather sensor? + {.t = 17, .s = " 17"}, // Unknown - Outside weather sensor? {.t = 18, .s = "HGI"}, // Honeywell Gateway Interface (HGI80, HGS80) {.t = 22, .s = "THM"}, // Thermostat with setpoint schedule control (DTS92E) {.t = 30, .s = "GWY"}, // Gateway (e.g. RFG100?) @@ -107,9 +107,9 @@ static data_t *decode_device_ids(const message_t *msg, data_t *data, int style) char buf[16] = {0}; if (style == 0) - decode_device_id(msg->device_id[i], buf, 16); + decode_device_id(msg->device_id[i], buf, sizeof(buf)); else { - sprintf(buf, "%02x%02x%02x", + snprintf(buf, sizeof(buf), "%02x%02x%02x", msg->device_id[i][0], msg->device_id[i][1], msg->device_id[i][2]); @@ -171,7 +171,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag uint8_t month = msg->payload[6]; uint8_t year[2] = { msg->payload[7], msg->payload[8] }; char time_str[256]; - sprintf(time_str, "%02d:%02d:%02d %02d-%02d-%04d", hour, minute, second, day, month, (year[0] << 8) | year[1]); + snprintf(time_str, sizeof(time_str), "%02d:%02d:%02d %02d-%02d-%04d", hour, minute, second, day, month, (year[0] << 8) | year[1]); data = data_append(data, "datetime", "", DATA_STRING, time_str, NULL); break; } @@ -181,17 +181,17 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag case 0x0008: { UNKNOWN_IF(msg->payload_length != 2); data = data_append(data, "domain_id", "", DATA_INT, msg->payload[0], NULL); - data = data_append(data, "demand", "", DATA_DOUBLE, msg->payload[1] / 200.0 /* 0xC8 */, NULL); + data = data_append(data, "demand", "", DATA_DOUBLE, msg->payload[1] * (1 / 200.0F) /* 0xC8 */, NULL); break; } case 0x3ef0: { UNKNOWN_IF(msg->payload_length != 3 && msg->payload_length != 6); switch (msg->payload_length) { case 3: - data = data_append(data, "status", "", DATA_DOUBLE, msg->payload[1] / 200.0 /* 0xC8 */, NULL); + data = data_append(data, "status", "", DATA_DOUBLE, msg->payload[1] * (1 / 200.0F) /* 0xC8 */, NULL); break; case 6: - data = data_append(data, "boiler_modulation_level", "", DATA_DOUBLE, msg->payload[1] / 200.0 /* 0xC8 */, NULL); + data = data_append(data, "boiler_modulation_level", "", DATA_DOUBLE, msg->payload[1] * (1 / 200.0F) /* 0xC8 */, NULL); data = data_append(data, "flame_status", "", DATA_INT, msg->payload[3], NULL); break; } @@ -201,17 +201,17 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag UNKNOWN_IF(msg->payload_length != 3); data = data_append(data, "zone", "", DATA_INT, msg->payload[0], NULL); // Observation: CM921 reports a very high setpoint during binding (0x7eff); packet: 143255c1230903017efff7 - data = data_append(data, "setpoint", "", DATA_DOUBLE, ((msg->payload[1] << 8) | msg->payload[2]) / 100.0, NULL); + data = data_append(data, "setpoint", "", DATA_DOUBLE, ((msg->payload[1] << 8) | msg->payload[2]) * (1 / 100.0F), NULL); break; } case 0x1100: { UNKNOWN_IF(msg->payload_length != 5 && msg->payload_length != 8); data = data_append(data, "domain_id", "", DATA_INT, msg->payload[0], NULL); - data = data_append(data, "cycle_rate", "", DATA_DOUBLE, msg->payload[1] / 4.0, NULL); - data = data_append(data, "minimum_on_time", "", DATA_DOUBLE, msg->payload[2] / 4.0, NULL); - data = data_append(data, "minimum_off_time", "", DATA_DOUBLE, msg->payload[3] / 4.0, NULL); + data = data_append(data, "cycle_rate", "", DATA_DOUBLE, msg->payload[1] * (1 / 4.0F), NULL); + data = data_append(data, "minimum_on_time", "", DATA_DOUBLE, msg->payload[2] * (1 / 4.0F), NULL); + data = data_append(data, "minimum_off_time", "", DATA_DOUBLE, msg->payload[3] * (1 / 4.0F), NULL); if (msg->payload_length == 8) - data = data_append(data, "proportional_band_width", "", DATA_DOUBLE, (msg->payload[5] << 8 | msg->payload[6]) / 100.0, NULL); + data = data_append(data, "proportional_band_width", "", DATA_DOUBLE, (msg->payload[5] << 8 | msg->payload[6]) * (1 / 100.0F), NULL); break; } case 0x0009: { @@ -227,18 +227,18 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag case 0x3B00: { UNKNOWN_IF(msg->payload_length != 2); data = data_append(data, "domain_id", "", DATA_INT, msg->payload[0], NULL); - data = data_append(data, "state", "", DATA_DOUBLE, msg->payload[1] / 200.0 /* 0xC8 */, NULL); + data = data_append(data, "state", "", DATA_DOUBLE, msg->payload[1] * (1 / 200.0F) /* 0xC8 */, NULL); break; } case 0x30C9: { - size_t num_zones = msg->payload_length/3; - for (size_t i=0; i < num_zones; i++) { - char name[256]; - snprintf(name, sizeof(name), "temperature (zone %u)", msg->payload[3*i]); - int16_t temp = msg->payload[3*i+1] << 8 | msg->payload[3*i+1]; - data = data_append(data, name, "", DATA_DOUBLE, temp/100.0, NULL); - } - break; + size_t num_zones = msg->payload_length / 3; + for (size_t i = 0; i < num_zones; i++) { + char name[256]; + snprintf(name, sizeof(name), "temperature (zone %u)", msg->payload[3 * i]); + int16_t temp = msg->payload[3 * i + 1] << 8 | msg->payload[3 * i + 2]; + data = data_append(data, name, "", DATA_DOUBLE, temp * (1 / 100.0F), NULL); + } + break; } case 0x1fd4: { int temp = (msg->payload[1] << 8) | msg->payload[2]; diff --git a/src/rtl_433/devices/ikea_sparsnas.c b/src/rtl_433/devices/ikea_sparsnas.c index a414d120..8e359879 100644 --- a/src/rtl_433/devices/ikea_sparsnas.c +++ b/src/rtl_433/devices/ikea_sparsnas.c @@ -129,7 +129,7 @@ static uint32_t ikea_sparsnas_brute_force_encryption(uint8_t buffer[18]) } } } - return 0; + return 0; } static int ikea_sparsnas_decode(r_device *decoder, bitbuffer_t *bitbuffer) diff --git a/src/rtl_433/devices/inkbird_ith20r.c b/src/rtl_433/devices/inkbird_ith20r.c index 0ad87202..6fe4de88 100644 --- a/src/rtl_433/devices/inkbird_ith20r.c +++ b/src/rtl_433/devices/inkbird_ith20r.c @@ -102,7 +102,7 @@ static int inkbird_ith20r_callback(r_device *decoder, bitbuffer_t *bitbuffer) 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]; + float battery_ok = msg[7] * 0.01f; uint16_t sensor_id = (msg[9] << 8 | msg[8]); float temperature = ((int16_t)(msg[11] << 8 | msg[10])) * 0.1f; float temperature_ext = ((int16_t)(msg[13] << 8 | msg[12])) * 0.1f; @@ -115,12 +115,12 @@ static int inkbird_ith20r_callback(r_device *decoder, bitbuffer_t *bitbuffer) data = data_make( "model", "", DATA_STRING, "Inkbird-ITH20R", "id", "", DATA_INT, sensor_id, - "battery", "Battery", DATA_INT, battery, + "battery_ok", "Battery", DATA_DOUBLE, battery_ok, "sensor_num", "", DATA_INT, sensor_num, - "mic", "Integrity", DATA_STRING, "CRC", "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature, "temperature_2_C", "Temperature2", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature_ext, "humidity", "Humidity", DATA_FORMAT, "%.1f %%", DATA_DOUBLE, humidity, + "mic", "Integrity", DATA_STRING, "CRC", NULL); /* clang-format on */ @@ -131,12 +131,12 @@ static int inkbird_ith20r_callback(r_device *decoder, bitbuffer_t *bitbuffer) static char const *const output_fields[] = { "model", "id", - "battery", + "battery_ok", "sensor_num", - "mic", "temperature_C", "temperature_2_C", "humidity", + "mic", NULL, }; diff --git a/src/rtl_433/devices/interlogix.c b/src/rtl_433/devices/interlogix.c index 3e8991c9..2d5ef54f 100644 --- a/src/rtl_433/devices/interlogix.c +++ b/src/rtl_433/devices/interlogix.c @@ -103,10 +103,7 @@ static int interlogix_decode(r_device *decoder, bitbuffer_t *bitbuffer) data_t *data; unsigned int row = 0; - char device_type_id[2]; char const *device_type; - char device_serial[7]; - char raw_message[7]; int low_battery; char const *f1_latch_state; char const *f2_latch_state; @@ -162,7 +159,8 @@ static int interlogix_decode(r_device *decoder, bitbuffer_t *bitbuffer) return DECODE_FAIL_MIC; } - sprintf(device_type_id, "%01x", (reverse8(message[2]) >> 4)); + char device_type_id[2]; + snprintf(device_type_id, sizeof(device_type_id), "%01x", (reverse8(message[2]) >> 4)); switch ((reverse8(message[2]) >> 4)) { case 0xa: device_type = "contact"; break; @@ -171,12 +169,14 @@ static int interlogix_decode(r_device *decoder, bitbuffer_t *bitbuffer) case 0x6: device_type = "heat"; break; case 0x9: device_type = "glass"; break; // switch1 changes from open to closed on trigger - default: device_type = "unknown"; return DECODE_FAIL_SANITY; break; + default: device_type = "unknown"; break; } - sprintf(device_serial, "%02x%02x%02x", reverse8(message[2]), reverse8(message[1]), reverse8(message[0])); + char device_serial[7]; + snprintf(device_serial, sizeof(device_serial), "%02x%02x%02x", reverse8(message[2]), reverse8(message[1]), reverse8(message[0])); - sprintf(raw_message, "%02x%02x%02x", message[3], message[4], message[5]); + char raw_message[7]; + snprintf(raw_message, sizeof(raw_message), "%02x%02x%02x", message[3], message[4], message[5]); // keyfob logic. see protocol description addendum for protocol exceptions if ((reverse8(message[2]) >> 4) == 0xf) { diff --git a/src/rtl_433/devices/intertechno.c b/src/rtl_433/devices/intertechno.c index 81b0252f..f3b9045f 100644 --- a/src/rtl_433/devices/intertechno.c +++ b/src/rtl_433/devices/intertechno.c @@ -28,7 +28,7 @@ static int intertechno_callback(r_device *decoder, bitbuffer_t *bitbuffer) return DECODE_ABORT_EARLY; char id_str[11]; - sprintf(id_str, "%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4]); + snprintf(id_str, sizeof(id_str), "%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4]); int slave = b[7] & 0x0f; int master = (b[7] & 0xf0) >> 4; int command = b[6] & 0x07; diff --git a/src/rtl_433/devices/lacrosse_tx141x.c b/src/rtl_433/devices/lacrosse_tx141x.c index d870be9f..5885e8ec 100644 --- a/src/rtl_433/devices/lacrosse_tx141x.c +++ b/src/rtl_433/devices/lacrosse_tx141x.c @@ -19,6 +19,7 @@ also TFA 30.3222.02 (a LaCrosse-TX141W). also TFA 30.3251.10 (a LaCrosse-TX141W). also some rebrand (ORIA WA50B) with a slightly longer timing, s.a. #2088 also TFA 30.3243.02 (a LaCrosse-TX141Bv3) +also LaCrosse TX141-Bv4 (seems identical to LaCrosse-TX141Bv3) LaCrosse Color Forecast Station (model C85845), or other LaCrosse product utilizing the remote temperature/humidity sensor TX141TH-Bv2 transmitting @@ -112,23 +113,6 @@ static int lacrosse_tx141x_decode(r_device *decoder, bitbuffer_t *bitbuffer) // try again for TX141W/TX145wsdth, require at least 2 out of 3-7 repeats. r = bitbuffer_find_repeated_row(bitbuffer, 2, 64); // 65 } - - bitbuffer_invert(bitbuffer); - - // For LACROSSE_TX141TH, do not require duplicate packets because it contains a CRC - // that is checked indepently. However, to simplify the code below, introduce a shortcut - // here instead of restructuring all following code: pre-select a row that fulfills - // protocol requirements (num_rows, bits_per_row and CRC) here and keep other sanity checks - // to the existing code below. - if (bitbuffer->num_rows <= 4) { - for (int row = 0; row < bitbuffer->num_rows; row++) { - if ((bitbuffer->bits_per_row[row] == 40 || bitbuffer->bits_per_row[row] == 41) && - lfsr_digest8_reflect(bitbuffer->bb[row], 4, 0x31, 0xf4) == bitbuffer->bb[row][4]) { - r = row; - } - } - } - if (r < 0) { return DECODE_ABORT_LENGTH; } @@ -157,6 +141,7 @@ static int lacrosse_tx141x_decode(r_device *decoder, bitbuffer_t *bitbuffer) device = LACROSSE_TX141BV3; } + bitbuffer_invert(bitbuffer); b = bitbuffer->bb[r]; if (device == LACROSSE_TX141W) { @@ -303,7 +288,7 @@ static int lacrosse_tx141x_decode(r_device *decoder, bitbuffer_t *bitbuffer) return 1; } -static char const *output_fields[] = { +static char const *const output_fields[] = { "model", "id", "channel", @@ -318,7 +303,7 @@ static char const *output_fields[] = { }; // note TX141W, TX145wsdth: m=OOK_PWM, s=256, l=500, r=1888, y=748 -r_device lacrosse_tx141x = { +r_device const lacrosse_tx141x = { .name = "LaCrosse TX141-Bv2, TX141TH-Bv2, TX141-Bv3, TX141W, TX145wsdth, (TFA, ORIA) sensor", .modulation = OOK_PULSE_PWM, .short_width = 208, // short pulse is 208 us + 417 us gap @@ -328,4 +313,4 @@ r_device lacrosse_tx141x = { .reset_limit = 1700, // maximum gap is 1250 us (long gap + longer sync gap on last repeat) .decode_fn = &lacrosse_tx141x_decode, .fields = output_fields, -}; \ No newline at end of file +}; diff --git a/src/rtl_433/devices/lacrosse_tx31u.c b/src/rtl_433/devices/lacrosse_tx31u.c index 1f542493..a32480cb 100644 --- a/src/rtl_433/devices/lacrosse_tx31u.c +++ b/src/rtl_433/devices/lacrosse_tx31u.c @@ -68,8 +68,8 @@ These readings have not been tested. #define BIT(pos) (1 << (pos)) #define CHECK_BIT(y, pos) ((0u == ((y) & (BIT(pos)))) ? 0u : 1u) -#define SET_LSBITS(len) (BIT(len) - 1) // the first len bits are '1' and the rest are '0' -#define BF_PREP(y, start, len) (((y)&SET_LSBITS(len)) << (start)) // Prepare a bitmask +#define SET_LSBITS(len) (BIT(len) - 1) // the first len bits are '1' and the rest are '0' +#define BF_PREP(y, start, len) (((y) & SET_LSBITS(len)) << (start)) // Prepare a bitmask #define BF_GET(y, start, len) (((y) >> (start)) & SET_LSBITS(len)) #define TX31U_MIN_LEN_BYTES 9 // assume at least one measurement @@ -161,7 +161,7 @@ static int lacrosse_tx31u_decode(r_device *decoder, bitbuffer_t *bitbuffer) } break; case RAIN: { int raw_rain = (nib1<<8) + (nib2<<4) + nib3; // count of contact closures - if ( !no_ext_sensor && raw_rain > 0) { // most of these do not have rain gauges. Surpress output if zero. + if ( !no_ext_sensor && raw_rain > 0) { // most of these do not have rain gauges. Suppress output if zero. data = data_append( data, "rain", "raw_rain", DATA_FORMAT, "%03x", DATA_INT, raw_rain, NULL); @@ -178,7 +178,7 @@ static int lacrosse_tx31u_decode(r_device *decoder, bitbuffer_t *bitbuffer) } } break; case WIND_MAX: { - int wind_input_lost = CHECK_BIT(nib1, 0); // a sensor was attched, but now not detected + int wind_input_lost = CHECK_BIT(nib1, 0); // a sensor was attached, but now not detected if ( !no_ext_sensor && !wind_input_lost ) { float wind_max = ((nib2<<4) + nib3) * 0.1f * 3.6; // wind values are decimal m/sec, convert to km/hr data = data_append( data, diff --git a/src/rtl_433/devices/maverick_et73x.c b/src/rtl_433/devices/maverick_et73x.c index afe085e6..4554c526 100644 --- a/src/rtl_433/devices/maverick_et73x.c +++ b/src/rtl_433/devices/maverick_et73x.c @@ -59,7 +59,7 @@ static int maverick_et73x_callback(r_device *decoder, bitbuffer_t *bitbuffer) // decode the inner manchester encoding bitbuffer_manchester_decode(bitbuffer, 0, 0, &mc, 104); - // we require 7 bytes 13 nibble rounded up (b[6] highest referance below) + // we require 7 bytes 13 nibble rounded up (b[6] highest reference below) if (mc.bits_per_row[0] < 52) { return DECODE_FAIL_SANITY; // manchester_decode fail } diff --git a/src/rtl_433/devices/neptune_r900.c b/src/rtl_433/devices/neptune_r900.c index 110f36f3..0da30450 100644 --- a/src/rtl_433/devices/neptune_r900.c +++ b/src/rtl_433/devices/neptune_r900.c @@ -111,7 +111,7 @@ static int neptune_r900_decode(r_device *decoder, bitbuffer_t *bitbuffer) int count = 0; /* - * Each group of four of these chips must be interpretted as a digit in base 6 + * Each group of four of these chips must be interpreted as a digit in base 6 * according to the following mapping: * 0011 -> 0 * 0101 -> 1 @@ -184,7 +184,7 @@ static int neptune_r900_decode(r_device *decoder, bitbuffer_t *bitbuffer) int leaknow = b[9]&0x03; // extra 24 bits ??? char extra[7]; - sprintf(extra,"%02x%02x%02x", b[10], b[11], b[12]); + snprintf(extra, sizeof(extra),"%02x%02x%02x", b[10], b[11], b[12]); /* clang-format off */ data_t *data = data_make( diff --git a/src/rtl_433/devices/norgo.c b/src/rtl_433/devices/norgo.c index 4f1ecf66..443b0ca8 100644 --- a/src/rtl_433/devices/norgo.c +++ b/src/rtl_433/devices/norgo.c @@ -141,10 +141,10 @@ static int norgo_decode(r_device *decoder, bitbuffer_t *bitbuffer) return DECODE_ABORT_EARLY; } - int xor = xor_bytes(b + 1, (bitbuffer->bits_per_row[0] - 15) / 8); - if (xor != 0xff) { // before invert 0 is ff + int xor_byte = xor_bytes(b + 1, (bitbuffer->bits_per_row[0] - 15) / 8); + if (xor_byte != 0xff) { // before invert 0 is ff decoder_logf_bitrow(decoder, 1, __func__, b, bitbuffer->bits_per_row[0], "XOR fail (%02x)", - xor); + xor_byte); return DECODE_FAIL_MIC; } diff --git a/src/rtl_433/devices/oil_watchman_advanced.c b/src/rtl_433/devices/oil_watchman_advanced.c index 2037c8cc..02c8324d 100644 --- a/src/rtl_433/devices/oil_watchman_advanced.c +++ b/src/rtl_433/devices/oil_watchman_advanced.c @@ -15,7 +15,8 @@ Watchman Sonic Advanced/Plus oil tank level monitor. Tested devices: -- Watchman Sonic Advanced +- Watchman Sonic Advanced, model code 0x0401 (seen on two devices) +- Tekelek, model code 0x0106 (seen on two devices) The devices uses GFSK with 500 us long and short pulses. Using -Y minmax should be sufficient to get it to work. @@ -23,35 +24,34 @@ Using -Y minmax should be sufficient to get it to work. Total length of message including preamble is 192 bits. The format might be most easily summarised in a BitBench string: - PRE: 40b SYNC: 16h MODEL: 24h ID: 24d 8h TEMP: 8h 16h DEPTH: 8d 32h CRC: 16h + PRE: 40b SYNC: 16h LEN:8d MODEL:16h ID:24d 8h TEMP:8h ?:16h DEPTH:8d VER:32h CRC:16h Data Layout: - 40 bits of preamble, i.e. 10101010 etc. -- 0x2dd4 - 'standard' sync word +- 2 byte of 0x2dd4 - 'standard' sync word - 1 byte - message length, fixed 0x0e (14) -- 2 byte - fixed 0x0401 - presumably a model identifier, common at least to the two devices we have tested this on -- 0x0e0401 - presumably a model identifier, common at least to the two devices we have tested this on +- 2 byte - fixed 0x0401 or 0x0106 - presumably a model identifier, common at least to the devices we have tested - 3 byte integer serial number - as printed on a label attached to the device itself - 1 byte status: - 0xC0 - during the first 20ish minutes after sync with the receiver when the device is transmitting once per second - 0x80 - the first one or two transmissions after the sync period when the device seems to be calibrating itself - 0x98 - normal, live value that you'll see on every transmission when the device is up and running - 1 byte temperature, in intervals of 0.5 degrees, offset by 0x48 -- two more varying bytes which could be the raw sensor reading +- 2 byte - varying bytes which could be the raw sensor reading - 1 byte integer depth (i.e. the distance between the sensor and the oil in the tank) -- 0x01050300 - 4 bytes of constant values which could be a version number? (1.5.3.0) +- 4 byte of 0x01050300 - constant values which could be a version number? (1.5.3.0) - 2 byte CRC-16 poly 0x8005 init 0 */ static int oil_watchman_advanced_decode(r_device *decoder, bitbuffer_t *bitbuffer) { static uint8_t const PREAMBLE_SYNC_LENGTH_BITS = 40; - static uint8_t const HEADER_LENGTH_BITS = 24; - static uint8_t const BODY_LENGTH_BITS = 128; + static uint8_t const HEADER_LENGTH_BITS = 8; + static uint8_t const BODY_LENGTH_BITS = 144; // no need to match all the preamble; 24 bits worth should do // include part of preamble, sync-word, length, message identifier - uint8_t const preamble_pattern[] = {0xaa, 0xaa, 0xaa, 0x2d, 0xd4, 0x0e, 0x04, 0x01}; + uint8_t const preamble_pattern[] = {0xaa, 0xaa, 0xaa, 0x2d, 0xd4, 0x0e}; unsigned bitpos = 0; int events = 0; @@ -67,13 +67,20 @@ static int oil_watchman_advanced_decode(r_device *decoder, bitbuffer_t *bitbuffe uint8_t *b = msg; if (crc16(b, (BODY_LENGTH_BITS + HEADER_LENGTH_BITS) / 8, 0x8005, 0) != 0) { + decoder_log(decoder, 2, __func__, "failed CRC check"); return DECODE_FAIL_MIC; } + int mcode = (b[1] << 8) | b[2]; + if (mcode != 0x0401 && mcode != 0x0106) { + decoder_logf(decoder, 1, __func__, "Unknown model code %04x", mcode); + return DECODE_FAIL_SANITY; + } + // as printed on the side of the unit uint32_t serial = (b[3] << 16) | (b[4] << 8) | b[5]; uint8_t status = b[6]; - float temperature = (float)(int8_t)(0.5 * (b[7] - 0x48)); // truncate to whole number + float temperature = (b[7] - 0x48) / 2; // truncate to whole number uint8_t depth = b[10]; /* clang-format off */ @@ -103,7 +110,7 @@ static char const *const output_fields[] = { }; r_device const oil_watchman_advanced = { - .name = "Watchman Sonic Advanced / Plus", + .name = "Watchman Sonic Advanced / Plus, Tekelek", .modulation = FSK_PULSE_PCM, .short_width = 500, .long_width = 500, diff --git a/src/rtl_433/devices/oregon_scientific.c b/src/rtl_433/devices/oregon_scientific.c index d16316db..0674bd8a 100644 --- a/src/rtl_433/devices/oregon_scientific.c +++ b/src/rtl_433/devices/oregon_scientific.c @@ -12,8 +12,10 @@ #include "decoder.h" -/// Documentation for Oregon Scientific protocols can be found here: -/// http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf +// Documentation for Oregon Scientific protocols can be found here: +// http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf +// +// note that at least for THN132N, THGR122N, THGR810 valid channel numbers are 1, 2, 4. // Sensors ID #define ID_THGR122N 0x1d20 #define ID_THGR968 0x1d30 @@ -22,6 +24,7 @@ #define ID_RGR968 0x2d10 #define ID_THR228N 0xec40 #define ID_THN132N 0xec40 // same as THR228N but different packet size +#define ID_AWR129 0xec41 // similar to THR228N, but an extra 100s digit #define ID_RTGN318 0x0cc3 // warning: id is from 0x0cc3 and 0xfcc3 #define ID_RTGN129 0x0cc3 // same as RTGN318 but different packet size #define ID_THGR810 0xf824 // This might be ID_THGR81, but what's true is lost in (git) history @@ -50,7 +53,9 @@ static float get_os_temperature(unsigned char *message) { float temp_c = 0; temp_c = (((message[5] >> 4) * 100) + ((message[4] & 0x0f) * 10) + ((message[4] >> 4) & 0x0f)) / 10.0F; - // Correct 0x0f to 0x08: + // The AWR129 BBQ thermometer has another digit to represent higher temperatures than what weather stations would observe. + temp_c += (message[5] & 0x07) * 100.0F; + // 0x08 is the sign bit if (message[5] & 0x08) { temp_c = -temp_c; } @@ -88,37 +93,9 @@ static unsigned int get_os_uv(unsigned char *message) return uvidx; } -static unsigned int get_os_channel(unsigned char *message, unsigned int sensor_id) +static unsigned cm180i_power(uint8_t const *msg, unsigned int offset) { - // sensor ID included to support sensors with channel in different position - int channel = 0; - channel = ((message[2] >> 4) & 0x0f); - if ((channel == 4) - && (sensor_id & 0x0fff) != ID_RTGN318 - && sensor_id != ID_THGR810 - && (sensor_id & 0x0fff) != ID_RTHN129 - && sensor_id != ID_THGR328N) - channel = 3; // sensor 3 channel number is 0x04 - return channel; -} - -static unsigned int get_os_battery(unsigned char *message) -{ - int battery_low = 0; - battery_low = (message[3] >> 2 & 0x01); - return battery_low; -} - -static unsigned int get_os_rollingcode(unsigned char *message) -{ - int rc = 0; - rc = (message[2] & 0x0F) + (message[3] & 0xF0); - return rc; -} - -static unsigned short int cm180i_power(uint8_t const *msg, unsigned int offset) -{ - unsigned short int val = 0; + unsigned val = 0; val = (msg[4+offset*2] << 8) | (msg[3+offset*2] & 0xF0); // tested across situations varying from 700 watt to more than 8000 watt to @@ -127,15 +104,15 @@ static unsigned short int cm180i_power(uint8_t const *msg, unsigned int offset) return val; } -static unsigned long long cm180i_total(uint8_t const *msg) +static uint64_t cm180i_total(uint8_t const *msg) { - unsigned long long val = 0; + uint64_t val = 0; if ((msg[1] & 0x0F) == 0) { // Sensor returns total only if nibble#4 == 0 - val = (unsigned long long)msg[14] << 40; - val += (unsigned long long)msg[13] << 32; - val += (unsigned long)msg[12] << 24; - val += (unsigned long)msg[11] << 16; + val = (uint64_t)msg[14] << 40; + val += (uint64_t)msg[13] << 32; + val += (uint32_t)msg[12] << 24; + val += msg[11] << 16; val += msg[10] << 8; val += msg[9]; } @@ -147,9 +124,9 @@ static uint8_t swap_nibbles(uint8_t byte) return (((byte&0xf) << 4) | (byte >> 4)); } -static unsigned short int cm180_power(uint8_t const *msg) +static unsigned cm180_power(uint8_t const *msg) { - unsigned short int val = 0; + unsigned val = 0; val = (msg[4] << 8) | (msg[3] & 0xF0); // tested across situations varying from 700 watt to more than 8000 watt to // get same value as showed in physical CM180 panel (exactly equals to 1+1/160) @@ -157,15 +134,15 @@ static unsigned short int cm180_power(uint8_t const *msg) return val; } -static unsigned long long cm180_total(uint8_t const *msg) +static uint64_t cm180_total(uint8_t const *msg) { - unsigned long long val = 0; + uint64_t val = 0; if ((msg[1] & 0x0F) == 0) { // Sensor returns total only if nibble#4 == 0 - val = (unsigned long long)msg[10] << 40; - val += (unsigned long long)msg[9] << 32; - val += (unsigned long)msg[8] << 24; - val += (unsigned long)msg[7] << 16; + val = (uint64_t)msg[10] << 40; + val += (uint64_t)msg[9] << 32; + val += (uint32_t)msg[8] << 24; + val += msg[7] << 16; val += msg[6] << 8; val += msg[5]; } @@ -261,17 +238,21 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff } int msg_bits = databits.bits_per_row[0]; - int sensor_id = (msg[0] << 8) | msg[1]; - decoder_logf(decoder, 1, __func__,"Found sensor_id (%08x)",sensor_id); + int sensor_id = (msg[0] << 8) | msg[1]; + int channel = (msg[2] >> 4) & 0x0f; + int device_id = (msg[2] & 0x0f) | (msg[3] & 0xf0); + int battery_low = (msg[3] >> 2) & 0x01; + + decoder_logf(decoder, 1, __func__,"Found sensor type (%08x)", sensor_id); if ((sensor_id == ID_THGR122N) || (sensor_id == ID_THGR968)) { if (validate_os_v2_message(decoder, msg, 76, msg_bits, 15) != 0) return 0; /* clang-format off */ data = data_make( "model", "", DATA_STRING, (sensor_id == ID_THGR122N) ? "Oregon-THGR122N" : "Oregon-THGR968", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, get_os_temperature(msg), "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), NULL); @@ -288,9 +269,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-WGR968", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "wind_max_m_s", "Gust", DATA_FORMAT, "%2.1f m/s",DATA_DOUBLE, gustWindspeed, "wind_avg_m_s", "Average", DATA_FORMAT, "%2.1f m/s",DATA_DOUBLE, avgWindspeed, "wind_dir_deg", "Direction", DATA_FORMAT, "%3.1f degrees",DATA_DOUBLE, quadrant, @@ -319,9 +300,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-BHTR968", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), "pressure_hPa", "Pressure", DATA_FORMAT, "%.0f hPa", DATA_DOUBLE, pressure, @@ -339,9 +320,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-BTHR918", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), "pressure_hPa", "Pressure", DATA_FORMAT, "%.0f hPa", DATA_DOUBLE, pressure, @@ -358,9 +339,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-RGR968", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "rain_rate_mm_h", "Rain Rate", DATA_FORMAT, "%.02f mm/h", DATA_DOUBLE, rain_rate, "rain_mm", "Total Rain", DATA_FORMAT, "%.02f mm", DATA_DOUBLE, total_rain, NULL); @@ -368,16 +349,17 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff decoder_output_data(decoder, data); return 1; } - else if (sensor_id == ID_THR228N && msg_bits == 76) { + else if ((sensor_id == ID_THR228N || sensor_id == ID_AWR129) && msg_bits == 76) { if (validate_os_v2_message(decoder, msg, 76, msg_bits, 12) != 0) return 0; float temp_c = get_os_temperature(msg); /* clang-format off */ data = data_make( - "model", "", DATA_STRING, "Oregon-THR228N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "model", "", DATA_COND, sensor_id == ID_THR228N, DATA_STRING, "Oregon-THR228N", + "model", "", DATA_COND, sensor_id == ID_AWR129, DATA_STRING, "Oregon-AWR129", + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, NULL); /* clang-format on */ @@ -402,9 +384,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-THN132N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, NULL); /* clang-format on */ @@ -418,9 +400,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-RTGN129", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, // 1 to 5 + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), NULL); @@ -434,9 +416,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-RTGR328N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, // 1 to 5 + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, get_os_temperature(msg), "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), NULL); @@ -457,15 +439,15 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff int seconds = ((msg[4] & 0x0F) * 10) + ((msg[4] & 0xF0) >> 4); char clock_str[24]; - sprintf(clock_str, "%04d-%02d-%02dT%02d:%02d:%02d", + snprintf(clock_str, sizeof(clock_str), "%04d-%02d-%02dT%02d:%02d:%02d", year, month, day, hours, minutes, seconds); /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-RTGR328N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, // 1 to 5 + "battery_ok", "Battery", DATA_INT, !battery_low, "radio_clock", "Radio Clock", DATA_STRING, clock_str, NULL); /* clang-format on */ @@ -478,9 +460,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-RTGN318", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, // 1 to 5 + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), NULL); @@ -499,9 +481,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, (sensor_id == ID_THN129) ? "Oregon-THN129" : "Oregon-RTHN129", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, // 1 to 5 + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, NULL); /* clang-format on */ @@ -523,9 +505,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-BTHGN129", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, // 1 to 5 + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), "pressure_hPa", "Pressure", DATA_FORMAT, "%.02f hPa", DATA_DOUBLE, pressure, @@ -552,10 +534,10 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-UVR128", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), + "id", "House Code", DATA_INT, device_id, "uv", "UV Index", DATA_FORMAT, "%u", DATA_INT, uvidx, - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), - //"channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), + "battery_ok", "Battery", DATA_INT, !battery_low, + //"channel", "Channel", DATA_INT, channel, NULL); /* clang-format on */ decoder_output_data(decoder, data); @@ -567,9 +549,9 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-THGR328N", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), // 1 to 5 - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, // 1 to 5 + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, get_os_temperature(msg), "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, get_os_humidity(msg), NULL); @@ -578,7 +560,7 @@ static int oregon_scientific_v2_1_decode(r_device *decoder, bitbuffer_t *bitbuff return 1; } else if (msg_bits > 16) { - decoder_logf_bitrow(decoder, 1, __func__, msg, msg_bits, "Unrecognized Oregon Scientific v2.1 message (device ID %4x)", sensor_id); + decoder_logf_bitrow(decoder, 1, __func__, msg, msg_bits, "Unrecognized Oregon Scientific v2.1 message (sensor type %04x)", sensor_id); } else { decoder_log_bitrow(decoder, 1, __func__, b, bitbuffer->bits_per_row[0], "Possible Oregon Scientific v2.1 message, but sync nibble wasn't found. Raw"); @@ -659,7 +641,11 @@ static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer bitbuffer_extract_bytes(bitbuffer, 0, msg_pos, msg, msg_len); reflect_nibbles(msg, (msg_len + 7) / 8); - int sensor_id = (msg[0] << 8) | msg[1]; + int sensor_id = (msg[0] << 8) | msg[1]; // not for CM sensor types + int channel = (msg[2] >> 4) & 0x0f; // not for CM sensor types + int device_id = (msg[2] & 0x0f) | (msg[3] & 0xf0); // not for CM sensor types + int battery_low = (msg[3] >> 2) & 0x01; // not for CM sensor types + if (sensor_id == ID_THGR810 || sensor_id == ID_THGR810a) { if (validate_os_checksum(decoder, msg, 15) != 0) return DECODE_FAIL_MIC; @@ -678,9 +664,9 @@ static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-THGR810", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, NULL); @@ -695,9 +681,9 @@ static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-THN802", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Celsius", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, NULL); /* clang-format on */ @@ -711,9 +697,9 @@ static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-UV800", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "uv", "UV Index", DATA_FORMAT, "%u", DATA_INT, uvidx, NULL); /* clang-format on */ @@ -744,9 +730,9 @@ static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-PCR800", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "rain_rate_in_h", "Rain Rate", DATA_FORMAT, "%5.1f in/h", DATA_DOUBLE, rain_rate, "rain_in", "Total Rain", DATA_FORMAT, "%7.3f in", DATA_DOUBLE, total_rain, NULL); @@ -762,9 +748,9 @@ static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-PCR800a", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "rain_rate_in_h", "Rain Rate", DATA_FORMAT, "%3.1f in/h", DATA_DOUBLE, rain_rate, "rain_in", "Total Rain", DATA_FORMAT, "%3.1f in", DATA_DOUBLE, total_rain, NULL); @@ -799,9 +785,9 @@ static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer /* clang-format off */ data = data_make( "model", "", DATA_STRING, "Oregon-WGR800", - "id", "House Code", DATA_INT, get_os_rollingcode(msg), - "channel", "Channel", DATA_INT, get_os_channel(msg, sensor_id), - "battery_ok", "Battery", DATA_INT, !get_os_battery(msg), + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, "wind_max_m_s", "Gust", DATA_FORMAT, "%2.1f m/s",DATA_DOUBLE, gustWindspeed, "wind_avg_m_s", "Average", DATA_FORMAT, "%2.1f m/s",DATA_DOUBLE, avgWindspeed, "wind_dir_deg", "Direction", DATA_FORMAT, "%3.1f degrees",DATA_DOUBLE, quadrant, @@ -853,8 +839,8 @@ static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer int id = msg[2] << 8 | (msg[1] & 0xF0); int batt_low = (msg[3] & 0x1); // 8th bit instead of 6th commonly used for other devices - unsigned short int ipower = cm180_power(msg); - unsigned long long itotal = cm180_total(msg); + unsigned ipower = cm180_power(msg); + uint64_t itotal = cm180_total(msg); float total_energy = itotal / 3600.0 / 1000.0; if (valid == 0) { /* clang-format off */ @@ -885,13 +871,13 @@ static int oregon_scientific_v3_decode(r_device *decoder, bitbuffer_t *bitbuffer int id = msg[2] << 8 | (msg[1] & 0xF0); int batt_low = (msg[3] & 0x40)?1:0; // 8th bit instead of 6th commonly used for other devices - unsigned short int ipower1 = cm180i_power(msg,0); - unsigned short int ipower2 = cm180i_power(msg,1); - unsigned short int ipower3 = cm180i_power(msg,2); - unsigned long long itotal= 0; + unsigned ipower1 = cm180i_power(msg,0); + unsigned ipower2 = cm180i_power(msg,1); + unsigned ipower3 = cm180i_power(msg,2); + uint64_t itotal= 0; if (msg_len >= 140) itotal= cm180i_total(msg); - // per hour and in kilowat + // Convert `itotal` which is in Ws (or J) to kWh unit. float total_energy = itotal / 3600.0 / 1000.0; if (valid == 0) { @@ -971,4 +957,4 @@ r_device const oregon_scientific = { .reset_limit = 2400, .decode_fn = &oregon_scientific_decode, .fields = output_fields, -}; \ No newline at end of file +}; diff --git a/src/rtl_433/devices/regency_fan.c b/src/rtl_433/devices/regency_fan.c index 2b087158..31ab4e24 100644 --- a/src/rtl_433/devices/regency_fan.c +++ b/src/rtl_433/devices/regency_fan.c @@ -119,23 +119,23 @@ static int regency_fan_decode(r_device *decoder, bitbuffer_t *bitbuffer) switch (command) { case 1: // 1 is the command to STOP - sprintf(value_string, "stop"); + snprintf(value_string, sizeof(value_string), "stop"); break; case 2: // 2 is the command to change fan speed - sprintf(value_string, "speed %d", value); + snprintf(value_string, sizeof(value_string), "speed %d", value); break; case 4: // 4 is the command to change the light intensity - sprintf(value_string, "%d %%", value); + snprintf(value_string, sizeof(value_string), "%d %%", value); break; case 5: // 5 is the command to set the light delay - sprintf(value_string, "%s", value == 0 ? "off" : "on"); + snprintf(value_string, sizeof(value_string), "%s", value == 0 ? "off" : "on"); break; case 6: // 6 is the command to change fan direction - sprintf(value_string, "%s", value == 0x07 ? "clockwise" : "counter-clockwise"); + snprintf(value_string, sizeof(value_string), "%s", value == 0x07 ? "clockwise" : "counter-clockwise"); break; default: diff --git a/src/rtl_433/devices/revolt_nc5462.c b/src/rtl_433/devices/revolt_nc5462.c index 7273df97..17066bdf 100644 --- a/src/rtl_433/devices/revolt_nc5462.c +++ b/src/rtl_433/devices/revolt_nc5462.c @@ -65,6 +65,9 @@ static int revolt_nc5462_decode(r_device *decoder, bitbuffer_t *bitbuffer) uint8_t *b = bitbuffer->bb[0]; int sum = add_bytes(b, 11); + if (sum == 0) { + return DECODE_FAIL_SANITY; + } int chk = b[11]; if ((sum & 0xff) != chk) { return DECODE_FAIL_MIC; diff --git a/src/rtl_433/devices/schou_72543_rain.c b/src/rtl_433/devices/schou_72543_rain.c new file mode 100644 index 00000000..dbf2fcf8 --- /dev/null +++ b/src/rtl_433/devices/schou_72543_rain.c @@ -0,0 +1,122 @@ +/** @file + Schou 72543 Day Rain Gauge. + + contributed by Jesper M. Nielsen + discovered by Jesper M. Nielsen + based upon ambient_weather.c + + 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. +*/ + +#include "decoder.h" + +/** +Decode Schou 72543 Rain Gauge, DAY series. + +Devices supported: + +- Schou 72543 Rain Gauge, DAY Series. +- Motonet MTX rain gauge (Product code: 86-01352) sold in Finland. +- MarQuant Wireless Rain Gauge (Product code: 014369) sold by JULA AB, Sweden. + +This decoder handles the 433mhz rain-thermometer. + +Codes example: {66}50fc467b7f9a832a8, {65}a1f88cf6ff3506550, {70}a1f88cf6ff3506557c + + {66}: [ 0 ] [ 1010 0001 1111 1000 ] [ 1000 ] [ 1100 ] [ 1111 0110 ] [ 1111 1111 ] [ 0011 0101 ] [ 0000 0110 ] [ 0101 0101 ] [ 0 ] + {65}: [ 1010 0001 1111 1000 ] [ 1000 ] [ 1100 ] [ 1111 0110 ] [ 1111 1111 ] [ 0011 0101 ] [ 0000 0110 ] [ 0101 0101 ] [ 0 ] + {70}: [ 1010 0001 1111 1000 ] [ 1000 ] [ 1100 ] [ 1111 0110 ] [ 1111 1111 ] [ 0011 0101 ] [ 0000 0110 ] [ 0101 0101 ] [ 0111 11 ] + KEY: [ 0 ] [ IIII IIII IIII IIII ] [ SSSS ] [ NNNN ] [ rrrr rrrr ] [ RRRR RRRR ] [ tttt tttt ] [ TTTT TTTT ] [ CCCC CCCC ] [ 0??? ?? ] + +- 0: Always zero +- ?: Either 1 or 0 +- I: 16 bit random ID. Resets to new value after every battery change +- S: Status bits + [ X--- ]: Battery status: 0: OK, 1: Low battery + [ -X-- ]: Repeated signal: 0: New, 1: Repeat of last message (4 repeats will happen after battery replacement) + [ --XX ]: Assumed always to be 0 +- N: 4 bit running count. Increased by 2 every value incremented by 2 every message, i.e. 0, 2, 4, 6, 8, a, c, e, 0, 2... +- Rr: 16 bit Rainfall in 1/10 millimeters per count. Initial value fff6 = 6552.6 mm rain + r: lower 8 bit, initializes to f6 + R: Upper 8 bit, initializes to ff +- Tt: 16 bit temperature. + t: lower 8 bit + T: Upper 8 bit +- C: Checksum. Running 8 bit sum of the data left of the checksum. + E.g. {65}a1f88cf6ff3506'55'0 Checksum is 55 obtained as ( a1 + f8 + 8c + f6 + ff + 35 + 06 ) = 455 i.e. 55 + +*/ + +static int schou_72543_rain_decode(r_device *decoder, bitbuffer_t *bitbuffer) +{ + // Full data is 3 rows, two are required for data validation + if (bitbuffer->num_rows < 2) { + return DECODE_ABORT_LENGTH; + } + + // Check if the first 64 bits of at least two rows are alike + int row = bitbuffer_find_repeated_prefix(bitbuffer, 2, 64); + if (row < 0) { + return DECODE_ABORT_EARLY; + } + + // Load bitbuffer data and validate checksum + uint8_t *b = bitbuffer->bb[row]; + int micsum = b[7]; // Checksum as read + int calsum = add_bytes(b, 7) & 0x0FF; // Checksum as calculated, accounting for the lowest 8 bit + + if (micsum != calsum) { + decoder_logf_bitrow(decoder, 1, __func__, b, 65, "Checksum error, expected: %02x calculated: %02x", micsum, calsum); + return DECODE_FAIL_MIC; + } + + // Decode message + int device_id = (b[0] << 8) | b[1]; // Assuming little endian, but it not important as the value is random + int battery_low = (b[2] & 0x80) > 0; // if one, battery is low + int message_repeat = (b[2] & 0x40) > 0; // if one, message is a repeat (startup after batteries are replaced) + int message_counter = (b[2] & 0x0e) >> 1; // 3 bit counter (rather than 4 bit incrementing by 2 each time + float rain_mm = ((b[4] << 8) | b[3]) * 0.1f; // 0.0 to 6553.5 mm + float temperature_F = (((b[6] << 8) | b[5]) - 900) * 0.1f; // -40.0 to +158 degF + + /* clang-format off */ + data_t *data = data_make( + "model", "", DATA_STRING, "Schou-72543", + "id", "ID", DATA_INT, device_id, + "temperature_F", "Temperature", DATA_FORMAT, "%.1f F", DATA_DOUBLE, temperature_F, + "rain_mm", "Rain", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, + "battery_ok", "Battery_ok", DATA_INT, !battery_low, + "msg_counter", "Counter", DATA_INT, message_counter, + "msg_repeat", "Msg_repeat", DATA_INT, message_repeat, + "mic", "Integrity", DATA_STRING, "CHECKSUM", + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; +} + +static char const *const output_fields[] = { + "model", + "id", + "temperature_F", + "rain_mm", + "battery_ok", + "msg_counter", + "msg_repeat", + "mic", + NULL, +}; + +r_device const schou_72543_rain = { + .name = "Schou 72543 Day Rain Gauge, Motonet MTX Rain, MarQuant Rain Gauge", + .modulation = OOK_PULSE_PWM, + .short_width = 972, + .long_width = 2680, + .sync_width = 7328, + .reset_limit = 2712, + .decode_fn = &schou_72543_rain_decode, + .fields = output_fields, +}; diff --git a/src/rtl_433/devices/schraeder.c b/src/rtl_433/devices/schraeder.c index 183bc78a..946a5efe 100644 --- a/src/rtl_433/devices/schraeder.c +++ b/src/rtl_433/devices/schraeder.c @@ -34,12 +34,9 @@ Packet payload: 1 sync nibble and 8 bytes data, 17 nibbles: static int schraeder_decode(r_device *decoder, bitbuffer_t *bitbuffer) { - data_t *data; uint8_t b[8]; int serial_id; - char id_str[9]; int flags; - char flags_str[3]; int pressure; // mbar/hectopascal int temperature; // deg C @@ -60,11 +57,14 @@ static int schraeder_decode(r_device *decoder, bitbuffer_t *bitbuffer) flags = (b[0] & 0x0F) << 4 | b[1] >> 4; pressure = b[5] * 25; temperature = b[6] - 50; - sprintf(id_str, "%07X", serial_id); - sprintf(flags_str, "%02x", flags); + + char id_str[9]; + snprintf(id_str, sizeof(id_str), "%07X", serial_id); + char flags_str[3]; + snprintf(flags_str, sizeof(flags_str), "%02x", flags); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Schrader", "type", "", DATA_STRING, "TPMS", "flags", "", DATA_STRING, flags_str, @@ -133,8 +133,8 @@ static int schrader_EG53MA4_decode(r_device *decoder, bitbuffer_t *bitbuffer) flags = ((unsigned)b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; pressure = b[7] * 25; temperature = b[8]; - sprintf(id_str, "%06X", serial_id); - sprintf(flags_str, "%08x", flags); + snprintf(id_str, sizeof(id_str), "%06X", serial_id); + snprintf(flags_str, sizeof(flags_str), "%08x", flags); /* clang-format off */ data = data_make( @@ -267,7 +267,7 @@ static int schrader_SMD3MA4_decode(r_device *decoder, bitbuffer_t *bitbuffer) } char id_str[9]; - sprintf(id_str, "%06X", serial_id); + snprintf(id_str, sizeof(id_str), "%06X", serial_id); /* clang-format off */ data_t *data = data_make( diff --git a/src/rtl_433/devices/secplus_v1.c b/src/rtl_433/devices/secplus_v1.c index 011a7bfc..9c404179 100644 --- a/src/rtl_433/devices/secplus_v1.c +++ b/src/rtl_433/devices/secplus_v1.c @@ -206,7 +206,7 @@ static int secplus_v1_callback(r_device *decoder, bitbuffer_t *bitbuffer) gettimeofday(&cur_tv, NULL); timeval_subtract(&res_tv, &cur_tv, &cached_tv); - decoder_logf(decoder, 2, __func__, "res %12ld %8ld", res_tv.tv_sec, (long)res_tv.tv_usec); + decoder_logf(decoder, 2, __func__, "res %12ld %8ld", (long)res_tv.tv_sec, (long)res_tv.tv_usec); // is the data not expired if (res_tv.tv_sec == 0 && res_tv.tv_usec < CACHE_MAX_AGE) { @@ -319,7 +319,7 @@ static int secplus_v1_callback(r_device *decoder, bitbuffer_t *bitbuffer) snprintf(pin_s, sizeof(pin_s), "%04d", pin); } else if (10000 <= pin && pin <= 11029) { - strcat(pin_s, "enter"); + strcat(pin_s, "enter"); // NOLINT } int pin_suffix = 0; @@ -327,9 +327,9 @@ static int secplus_v1_callback(r_device *decoder, bitbuffer_t *bitbuffer) pin_suffix = (fixed / 1162261467) % 3; if (pin_suffix == 1) - strcat(pin_s, "#"); + strcat(pin_s, "#"); // NOLINT else if (pin_suffix == 2) - strcat(pin_s, "*"); + strcat(pin_s, "*"); // NOLINT // decoder_logf(decoder, 1, __func__, "pad_id=%d pin=%d pin_s=%s", pad_id, pin, pin_s); } diff --git a/src/rtl_433/devices/simplisafe.c b/src/rtl_433/devices/simplisafe.c index ff8d68d6..d21f46b3 100644 --- a/src/rtl_433/devices/simplisafe.c +++ b/src/rtl_433/devices/simplisafe.c @@ -55,8 +55,6 @@ static int ss_sensor_parser(r_device *decoder, bitbuffer_t *bitbuffer, int row) { data_t *data; uint8_t *b = bitbuffer->bb[row]; - char id[6]; - char extradata[30] = ""; // each row needs to have exactly 92 bits if (bitbuffer->bits_per_row[row] != 92) @@ -68,14 +66,19 @@ static int ss_sensor_parser(r_device *decoder, bitbuffer_t *bitbuffer, int row) if (((seq + state) & 0xff) != csum) return DECODE_FAIL_MIC; + char id[6]; ss_get_id(id, b); + char extradata[30]; if (state == 1) { - strcpy(extradata,"Contact Open"); + snprintf(extradata, sizeof(extradata), "Contact Open"); } else if (state == 2) { - strcpy(extradata,"Contact Closed"); + snprintf(extradata, sizeof(extradata), "Contact Closed"); } else if (state == 3) { - strcpy(extradata,"Alarm Off"); + snprintf(extradata, sizeof(extradata), "Alarm Off"); + } else { + //snprintf(extradata, sizeof(extradata), ""); + *extradata = '\0'; } /* clang-format off */ @@ -99,8 +102,6 @@ static int ss_pinentry_parser(r_device *decoder, bitbuffer_t *bitbuffer, int row { data_t *data; uint8_t *b = bitbuffer->bb[row]; - char id[6]; - char extradata[30]; // In a keypad message the pin is encoded in bytes 10 and 11 with the the digits each using 4 bits // However the bits are low order to high order int digits[5]; @@ -112,9 +113,11 @@ static int ss_pinentry_parser(r_device *decoder, bitbuffer_t *bitbuffer, int row digits[2] = (pinb & 0xf); digits[3] = ((pinb & 0xf0) >> 4); + char id[6]; ss_get_id(id, b); - sprintf(extradata, "Disarm Pin: %x%x%x%x", digits[0], digits[1], digits[2], digits[3]); + char extradata[30]; + snprintf(extradata, sizeof(extradata), "Disarm Pin: %x%x%x%x", digits[0], digits[1], digits[2], digits[3]); /* clang-format off */ data = data_make( @@ -136,23 +139,23 @@ static int ss_keypad_commands(r_device *decoder, bitbuffer_t *bitbuffer, int row { data_t *data; uint8_t *b = bitbuffer->bb[row]; - char id[6]; char extradata[30]; // = "Arming: "; if (b[10] == 0x6a) { - strcpy(extradata, "Arm System - Away"); + snprintf(extradata, sizeof(extradata), "Arm System - Away"); } else if (b[10] == 0xca) { - strcpy(extradata, "Arm System - Home"); + snprintf(extradata, sizeof(extradata), "Arm System - Home"); } else if (b[10] == 0x3a) { - strcpy(extradata, "Arm System - Canceled"); + snprintf(extradata, sizeof(extradata), "Arm System - Canceled"); } else if (b[10] == 0x2a) { - strcpy(extradata, "Keypad Panic Button"); + snprintf(extradata, sizeof(extradata), "Keypad Panic Button"); } else if (b[10] == 0x86) { - strcpy(extradata, "Keypad Menu Button"); + snprintf(extradata, sizeof(extradata), "Keypad Menu Button"); } else { - sprintf(extradata, "Unknown Keypad: %02x", b[10]); + snprintf(extradata, sizeof(extradata), "Unknown Keypad: %02x", b[10]); } + char id[6]; ss_get_id(id, b); /* clang-format off */ diff --git a/src/rtl_433/devices/smoke_gs558.c b/src/rtl_433/devices/smoke_gs558.c index 41315a9b..e4d56388 100644 --- a/src/rtl_433/devices/smoke_gs558.c +++ b/src/rtl_433/devices/smoke_gs558.c @@ -49,13 +49,11 @@ Also you always need to learn from the same primary. static int smoke_gs558_callback(r_device *decoder, bitbuffer_t *bitbuffer) { - data_t *data; uint8_t *b; int r; int learn = 0; int unit; // max 30 int id; - char code_str[7]; if (bitbuffer->num_rows < 3) return DECODE_ABORT_EARLY; // truncated transmission @@ -101,10 +99,11 @@ static int smoke_gs558_callback(r_device *decoder, bitbuffer_t *bitbuffer) if (id == 0 || id == 0x7fff) return DECODE_FAIL_SANITY; // reject min/max to reduce false positives - sprintf(code_str, "%02x%02x%02x", b[2], b[1], b[0]); + char code_str[7]; + snprintf(code_str, sizeof(code_str), "%02x%02x%02x", b[2], b[1], b[0]); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Smoke-GS558", "id" , "", DATA_INT, id, "unit", "", DATA_INT, unit, diff --git a/src/rtl_433/devices/somfy_iohc.c b/src/rtl_433/devices/somfy_iohc.c index 91f10798..ced436a6 100644 --- a/src/rtl_433/devices/somfy_iohc.c +++ b/src/rtl_433/devices/somfy_iohc.c @@ -120,6 +120,8 @@ static int somfy_iohc_decode(r_device *decoder, bitbuffer_t *bitbuffer) int msg_len = b[0] & 0x1f; if (len < msg_len + 3) return DECODE_ABORT_LENGTH; + if (msg_len < 8) + return DECODE_ABORT_LENGTH; len = msg_len + 3; int msg_end_flag = (b[0] & 0x80) >> 7; diff --git a/src/rtl_433/devices/somfy_rts.c b/src/rtl_433/devices/somfy_rts.c index 053b0434..00d8f2e3 100644 --- a/src/rtl_433/devices/somfy_rts.c +++ b/src/rtl_433/devices/somfy_rts.c @@ -53,7 +53,7 @@ On some devices (see #2356) there are two extra bytes for a total of 80 bits app There is a quirk with TEL-FIX wall-mounted remote control for RadioLoop Motor: It looks like the seed isn't random but actually the button code: 0x88 DOWN, 0x85 STOP, 0x86 UP. -The command is fixed to 0xf, which we use as idication that an actual command is in the seed. +The command is fixed to 0xf, which we use as indication that an actual command is in the seed. */ diff --git a/src/rtl_433/devices/steelmate.c b/src/rtl_433/devices/steelmate.c index 42e64cf4..bafc44ee 100644 --- a/src/rtl_433/devices/steelmate.c +++ b/src/rtl_433/devices/steelmate.c @@ -39,7 +39,6 @@ static int steelmate_callback(r_device *decoder, bitbuffer_t *bitbuffer) for (int row = 0; row < bitbuffer->num_rows; row++) { //Payload is inverted Manchester encoded, and reversed MSB/LSB order - char sensor_idhex[7]; uint8_t *b = bitbuffer->bb[row]; //Length must be 72 bits to be considered a valid packet @@ -51,7 +50,7 @@ static int steelmate_callback(r_device *decoder, bitbuffer_t *bitbuffer) continue; // DECODE_ABORT_EARLY //Preamble - uint8_t preAmble = ~reverse8(b[2]); + uint8_t preamble = ~reverse8(b[2]); //Sensor ID uint8_t id1 = ~reverse8(b[3]); @@ -68,14 +67,16 @@ static int steelmate_callback(r_device *decoder, bitbuffer_t *bitbuffer) //Checksum is a sum of all the other values uint8_t payload_checksum = ~reverse8(b[8]); - uint8_t calculated_checksum = preAmble + id1 + id2 + p1 + tempFahrenheit + tmpbattery_mV; + uint8_t calculated_checksum = preamble + id1 + id2 + p1 + tempFahrenheit + tmpbattery_mV; if (payload_checksum != calculated_checksum) continue; // DECODE_FAIL_MIC - int sensor_id = (id1 << 8) | id2; - sprintf(sensor_idhex, "0x%04x", sensor_id); + int sensor_id = (id1 << 8) | id2; float pressure_psi = p1 * 0.5f; - int battery_mV = tmpbattery_mV * 2; + int battery_mV = tmpbattery_mV * 2; + + char sensor_idhex[7]; + snprintf(sensor_idhex, sizeof(sensor_idhex), "0x%04x", sensor_id); /* clang-format off */ data_t *data = data_make( diff --git a/src/rtl_433/devices/tfa_marbella.c b/src/rtl_433/devices/tfa_marbella.c index 90c1724b..0c07c407 100644 --- a/src/rtl_433/devices/tfa_marbella.c +++ b/src/rtl_433/devices/tfa_marbella.c @@ -43,8 +43,7 @@ L - lsfr, byte reflected reverse galois with 0x31 key and generator static int tfa_marbella_callback(r_device *decoder, bitbuffer_t *bitbuffer) { unsigned bitpos = 0; - uint8_t msg[11], ic; - char serialnr_str[6 * 2 + 1]; + uint8_t msg[11]; uint8_t const preamble_pattern[] = {0xaa, 0x2d, 0xd4}; @@ -60,7 +59,7 @@ static int tfa_marbella_callback(r_device *decoder, bitbuffer_t *bitbuffer) return DECODE_FAIL_SANITY; // Rev-Galois with gen 0x31 and key 0x31 - ic = lfsr_digest8_reflect(&msg[3], 7, 0x31, 0x31); + uint8_t ic = lfsr_digest8_reflect(&msg[3], 7, 0x31, 0x31); if (ic != msg[10]) { return DECODE_FAIL_MIC; } @@ -71,7 +70,9 @@ static int tfa_marbella_callback(r_device *decoder, bitbuffer_t *bitbuffer) float temp_c = (temp_raw - 400) * 0.1f; int counter = (msg[6] & 0xF) >> 1; int serialnr = msg[3] << 16 | msg[4] << 8 | msg[5]; - sprintf(serialnr_str, "%06x", serialnr); + + char serialnr_str[6 * 2 + 1]; + snprintf(serialnr_str, sizeof(serialnr_str), "%06x", serialnr); /* clang-format off */ data_t *data = data_make( diff --git a/src/rtl_433/devices/thermopro_tp11.c b/src/rtl_433/devices/thermopro_tp11.c index 49198561..e9491503 100644 --- a/src/rtl_433/devices/thermopro_tp11.c +++ b/src/rtl_433/devices/thermopro_tp11.c @@ -1,5 +1,5 @@ /** @file - Thermopro TP-11 Thermometer. + ThermoPro TP-11 Thermometer. Copyright (C) 2017 Google Inc. @@ -8,18 +8,20 @@ the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ + +#include "decoder.h" + /** -Thermopro TP-11 Thermometer. +ThermoPro TP-11 Thermometer. normal sequence of bit rows: - [00] {33} db 41 57 c2 80 : 11011011 01000001 01010111 11000010 1 - [01] {33} db 41 57 c2 80 : 11011011 01000001 01010111 11000010 1 - [02] {33} db 41 57 c2 80 : 11011011 01000001 01010111 11000010 1 - [03] {32} db 41 57 c2 : 11011011 01000001 01010111 11000010 + [00] {33} db 41 57 c2 80 + [01] {33} db 41 57 c2 80 + [02] {33} db 41 57 c2 80 + [03] {32} db 41 57 c2 */ -#include "decoder.h" static int thermopro_tp11_sensor_callback(r_device *decoder, bitbuffer_t *bitbuffer) { diff --git a/src/rtl_433/devices/thermopro_tp12.c b/src/rtl_433/devices/thermopro_tp12.c index e29350ea..ea815cf6 100644 --- a/src/rtl_433/devices/thermopro_tp12.c +++ b/src/rtl_433/devices/thermopro_tp12.c @@ -1,5 +1,5 @@ /** @file - Thermopro TP-12 Thermometer. + ThermoPro TP-12 Thermometer. Copyright (C) 2017 Google Inc. @@ -8,29 +8,32 @@ the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ + +#include "decoder.h" + /** -Thermopro TP-12 Thermometer. +ThermoPro TP-12 Thermometer. A normal sequence for the TP12: [00] {0} : - [01] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [02] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [03] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [04] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [05] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [06] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [07] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [08] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [09] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [10] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [11] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [12] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [13] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [14] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [15] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [16] {41} 38 73 21 bb 81 80 : 00111000 01110011 00100001 10111011 10000001 1 - [17] {40} 38 73 21 bb 81 : 00111000 01110011 00100001 10111011 10000001 + [01] {41} 38 73 21 bb 81 80 + [02] {41} 38 73 21 bb 81 80 + [03] {41} 38 73 21 bb 81 80 + [04] {41} 38 73 21 bb 81 80 + [05] {41} 38 73 21 bb 81 80 + [06] {41} 38 73 21 bb 81 80 + [07] {41} 38 73 21 bb 81 80 + [08] {41} 38 73 21 bb 81 80 + [09] {41} 38 73 21 bb 81 80 + [10] {41} 38 73 21 bb 81 80 + [11] {41} 38 73 21 bb 81 80 + [12] {41} 38 73 21 bb 81 80 + [13] {41} 38 73 21 bb 81 80 + [14] {41} 38 73 21 bb 81 80 + [15] {41} 38 73 21 bb 81 80 + [16] {41} 38 73 21 bb 81 80 + [17] {40} 38 73 21 bb 81 Layout appears to be: @@ -41,11 +44,9 @@ Layout appears to be: */ -#include "decoder.h" - #define BITS_IN_VALID_ROW 41 -static int thermopro_tp12_sensor_callback(r_device *decoder, bitbuffer_t *bitbuffer) +static int thermopro_tp12_decode(r_device *decoder, bitbuffer_t *bitbuffer) { int temp1_raw, temp2_raw, row; float temp1_c, temp2_c; @@ -112,12 +113,12 @@ static char const *const output_fields[] = { }; r_device const thermopro_tp12 = { - .name = "Thermopro TP08/TP12/TP20 thermometer", + .name = "ThermoPro TP08/TP12/TP20 thermometer", .modulation = OOK_PULSE_PPM, .short_width = 500, .long_width = 1500, .gap_limit = 2000, .reset_limit = 4000, - .decode_fn = &thermopro_tp12_sensor_callback, + .decode_fn = &thermopro_tp12_decode, .fields = output_fields, }; diff --git a/src/rtl_433/devices/thermopro_tx2.c b/src/rtl_433/devices/thermopro_tx2.c index a62093b1..63ec0d14 100644 --- a/src/rtl_433/devices/thermopro_tx2.c +++ b/src/rtl_433/devices/thermopro_tx2.c @@ -8,7 +8,10 @@ the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -/** @fn int thermopro_tx2_decode(r_device *decoder, bitbuffer_t *bitbuffer) + +#include "decoder.h" + +/** ThermoPro TX2 sensor protocol. Note: this is the Prologue protocol with the battery low flag inverted. @@ -35,9 +38,6 @@ The data is grouped in 9 nibbles - humi: 8 bit always 11001100 (0xCC) if no humidity sensor is available */ - -#include "decoder.h" - static int thermopro_tx2_decode(r_device *decoder, bitbuffer_t *bitbuffer) { uint8_t *b; diff --git a/src/rtl_433/devices/thermopro_tx2c.c b/src/rtl_433/devices/thermopro_tx2c.c new file mode 100644 index 00000000..a1febaf9 --- /dev/null +++ b/src/rtl_433/devices/thermopro_tx2c.c @@ -0,0 +1,115 @@ +/** @file + ThermoPro TX-2C Outdoor Thermometer and humidity sensor. + + Copyright (C) 2023 igor@pele.tech. + Copyright (C) 2023 maxime@werlen.fr. + + 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. +*/ + +#include "decoder.h" + +/** +ThermoPro TX-2C Outdoor Thermometer. + +Example data: + + [00] { 7} 00 + [01] {45} 95 00 ff e0 a0 00 + [02] {45} 95 00 ff e0 a0 00 + [03] {45} 95 00 ff e0 a0 00 + [04] {45} 95 00 ff e0 a0 00 + [05] {45} 95 00 ff e0 a0 00 + [06] {45} 95 00 ff e0 a0 00 + [07] {45} 95 00 ff e0 a0 00 + [08] {36} 95 00 ff e0 a0 + +Data layout: + + [type] [id0] [id1] [flags] [temp0] [temp1] [temp2] [humi0] [humi1] [zero] [zero] [zero] + +- type: 4 bit fixed 1001 (9) or 0110 (5) +- id: 8 bit a random id that is generated when the sensor starts, could include battery status + the same batteries often generate the same id +- flags(3): is 1 when the battery is low, otherwise 0 (ok) +- flags(2): is 1 when the sensor sends a reading when pressing the button on the sensor +- flags(1,0): the channel number that can be set by the sensor (1, 2, 3, X) +- temp: 12 bit signed scaled by 10 +- humi: 8 bit always 00001010 (0x0A) if no humidity sensor is available +- zero : a trailing 12 bit fixed 000000000000 + +*/ + +static int thermopro_tx2c_decode(r_device *decoder, bitbuffer_t *bitbuffer) +{ + // Compare first four bytes of rows that have 45 or 36 bits. + int row = bitbuffer_find_repeated_row(bitbuffer, 4, 36); + if (row < 0) + return DECODE_ABORT_EARLY; + uint8_t *b = bitbuffer->bb[row]; + + if (bitbuffer->bits_per_row[row] > 45) + return DECODE_ABORT_LENGTH; + + // No need to decode/extract values for simple test + if ((!b[0] && !b[1] && !b[2] && !b[3]) + || (b[0] == 0xff && b[1] == 0xff && b[2] == 0xff && b[3] == 0xff)) { + decoder_log(decoder, 2, __func__, "DECODE_FAIL_SANITY data all 0x00 or 0xFF"); + return DECODE_FAIL_SANITY; + } + + // check existing 12 bit 0 trailer + if ((b[4] & 0x0F) != 0x00 || b[5] != 0x00) + return DECODE_FAIL_SANITY; + + // int type = b[0] >> 4; + int id = (((b[0] & 0xF) << 4) | (b[1] >> 4)); + int battery = (b[1] & 0x08) >> 3; + int button = (b[1] & 0x04) >> 2; + int channel = (b[1] & 0x03) + 1; + int temp_raw = (int16_t)((b[2] << 8) | b[3]); // uses sign-extend + float temp_c = (temp_raw >> 4) * 0.1f; + int humidity = (((b[3] & 0xF) << 4) | (b[4] >> 4)); + + /* clang-format off */ + data_t *data = data_make( + "model", "", DATA_STRING, "Thermopro-TX2C", + // "subtype", "", DATA_INT, type, + "id", "Id", DATA_INT, id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery, + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, + "humidity", "Humidity", DATA_COND, humidity != 0x0a, DATA_FORMAT, "%u %%", DATA_INT, humidity, + "button", "Button", DATA_INT, button, + NULL); + /* clang-format on */ + decoder_output_data(decoder, data); + return 1; +} + +static char const *const output_fields[] = { + "model", + // "subtype", + "id", + "channel", + "battery_ok", + "temperature_C", + "humidity", + "button", + NULL, +}; + +r_device const thermopro_tx2c = { + .name = "ThermoPro TX-2C Thermometer and Humidity sensor", + .modulation = OOK_PULSE_PPM, + .short_width = 1958, + .long_width = 3825, + .gap_limit = 3829, + .reset_limit = 8643, + .decode_fn = &thermopro_tx2c_decode, + .fields = output_fields, + .disabled = 1, // default disabled because there is no checksum +}; diff --git a/src/rtl_433/devices/tpms_abarth124.c b/src/rtl_433/devices/tpms_abarth124.c index d4c56c7f..26c1129c 100644 --- a/src/rtl_433/devices/tpms_abarth124.c +++ b/src/rtl_433/devices/tpms_abarth124.c @@ -27,7 +27,7 @@ Data layout (nibbles): - I: 32 bit ID - ?: 4 bit unknown (seems to change with status) - ?: 4 bit unknown (seems static) -- P: 8 bit Pressure (multiplyed by 1.38 = kPa) +- P: 8 bit Pressure (multiplied by 1.38 = kPa) - T: 8 bit Temperature (deg. C offset by 50) - S: Status? (first nibble seems static, second nibble seems to change with status) - C: 8 bit Checksum (Checksum8 XOR on bytes 0 to 8) @@ -38,11 +38,8 @@ Data layout (nibbles): static int tpms_abarth124_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; - char id_str[4 * 2 + 1]; - char flags[1 * 2 + 1]; int pressure; int temperature; int status; @@ -64,15 +61,18 @@ static int tpms_abarth124_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsi return 0; // DECODE_FAIL_MIC; } - sprintf(flags, "%02x", b[4]); pressure = b[5]; temperature = b[6]; status = b[7]; checksum = b[8]; - sprintf(id_str, "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]); + + char flags[1 * 2 + 1]; + snprintf(flags, sizeof(flags), "%02x", b[4]); + char id_str[4 * 2 + 1]; + snprintf(id_str, sizeof(id_str), "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Abarth-124Spider", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_ave.c b/src/rtl_433/devices/tpms_ave.c index e7fe0d7f..bf4bd423 100644 --- a/src/rtl_433/devices/tpms_ave.c +++ b/src/rtl_433/devices/tpms_ave.c @@ -32,11 +32,9 @@ Packet nibbles: static int tpms_ave_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; unsigned id; - char id_str[9 + 1]; int mode; int pressure_raw; double pressure; @@ -89,10 +87,11 @@ static int tpms_ave_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned r } pressure = ((double)pressure_raw - offset) * ratio; - sprintf(id_str, "%08x", id); + char id_str[9 + 1]; + snprintf(id_str, sizeof(id_str), "%08x", id); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "Model", DATA_STRING, "AVE", "type", "Type", DATA_STRING, "TPMS", "id", "Id", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_citroen.c b/src/rtl_433/devices/tpms_citroen.c index 12767bb1..173db4a6 100644 --- a/src/rtl_433/devices/tpms_citroen.c +++ b/src/rtl_433/devices/tpms_citroen.c @@ -31,13 +31,10 @@ Packet nibbles: static int tpms_citroen_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; int state; - char state_str[3]; unsigned id; - char id_str[9]; int flags; int repeat; int pressure; @@ -63,18 +60,21 @@ static int tpms_citroen_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsign return DECODE_FAIL_MIC; // bad checksum } - state = b[0]; // not covered by CRC - sprintf(state_str, "%02x", state); - id = (unsigned)b[1] << 24 | b[2] << 16 | b[3] << 8 | b[4]; - sprintf(id_str, "%08x", id); + state = b[0]; // not covered by CRC + id = (unsigned)b[1] << 24 | b[2] << 16 | b[3] << 8 | b[4]; flags = b[5] >> 4; repeat = b[5] & 0x0f; pressure = b[6]; temperature = b[7]; maybe_battery = b[8]; + char state_str[3]; + snprintf(state_str, sizeof(state_str), "%02x", state); + char id_str[9]; + snprintf(id_str, sizeof(id_str), "%08x", id); + /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Citroen", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_eezrv.c b/src/rtl_433/devices/tpms_eezrv.c index 5f470c40..46004dde 100644 --- a/src/rtl_433/devices/tpms_eezrv.c +++ b/src/rtl_433/devices/tpms_eezrv.c @@ -1,7 +1,7 @@ /** @file - EezTire E618 TPMS. + EezTire E618 TPMS and Carchet TPMS (same protocol). - Copyright (C) 2023 Bruno OCTAU (ProfBoc75) and Gliebig + Copyright (C) 2023 Bruno OCTAU (ProfBoc75), Gliebig, and Karen Suhm 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 @@ -12,32 +12,58 @@ #include "decoder.h" /** -EezTire E618 TPMS. +EezTire E618 TPMS and Carchet TPMS (same protocol). -Eez RV supported model TPMS10ATC (E618) : https://eezrvproducts.com/shop/ols/products/tpms10atc +Eez RV supported TPMS sensor model E618 : https://eezrvproducts.com/shop/ols/products/tpms-system-e518-anti-theft-replacement-sensor-1-ea +Carchet TPMS: http://carchet.easyofficial.com/carchet-rv-trailer-car-solar-tpms-tire-pressure-monitoring-system-6-sensor-lcd-display-p6.html -S.a issue #2384 +The device uses OOK (ASK) encoding. +The device sends a transmission every 1 second when quick deflation is detected, every 13 - 23 sec when quick inflation is detected, and every 4 min 40 s under steady state pressure. +A transmission starts with a preamble of 0x0000 and the packet is sent twice. -Data layout: +S.a issue #2384, #2657, #2063, #2677 - PRE CC IIIIII PP TT FF 00 +Data collection parameters on URH software were as follows: + Sensor frequency: 433.92 MHz + Sample rate: 2.0 MSps + Bandwidth: 2.0 Hz + Gain: 125 + + Modulation is ASK (OOK). Packets in URH arrive in the following format: + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [Pause: 897679 samples] + aaaaaaaa5956a5a5a6555aaa65959999a5aaaaaa [Pause: 6030 samples] + aaaaaaaa5956a5a5a6555aaa65959999a5aaaaaa [Pause: 11176528 samples] + + Decoding is Manchester I. After decoding, the packets look like this: + + 00000000000000000000000000000000000000 + 0000de332fc0b7553000 + 0000de332fc0b7553000 + + Using rtl_433 software, packets were detected using the following command line entry: + rtl_433 -X "n=Carchet,m=OOK_MC_ZEROBIT,s=50,l=50,r=1000,invert" -s 1M + + Data layout: + + PRE CC IIIIII PP TT FF FF - PRE : FFFF -- C : 8 bit CheckSum -- I: 24 bit ID +- C : 8 bit CheckSum, modulo 256 with overflow flag +- I: 24 bit little-endian ID - P: 8 bit pressure P * 2.5 = Pressure kPa - T: 8 bit temperature T - 50 = Temperature C -- F: 8 bit battery (not verified) and deflation pressure (for sure) flags +- F: 16 bit status flags: 0x8000 = low battery, 0x1000 = quick deflation, 0x3000 = quick inflation, 0x0000 = static/steady state -Raw Data exemple : +Raw Data example : ffff 8b 0d177e 8f 4a 10 00 Format string: - CHECKSUM:8h ID:24h KPA:8d TEMP:8d FLAG:4b 4h8h + CHECKSUM:8h ID:24h KPA:8d TEMP:8d FLAG:8b 8b -Decode exemple: +Decode example: CHECKSUM:8b ID:0d177e KPA:8f TEMP:4a FLAG:10 00 @@ -55,39 +81,63 @@ static int tpms_eezrv_decode(r_device *decoder, bitbuffer_t *bitbuffer) bitbuffer_invert(bitbuffer); pos = bitbuffer_search(bitbuffer, 0, pos, preamble_pattern, sizeof(preamble_pattern) * 8); if (pos >= bitbuffer->bits_per_row[0]) { - decoder_log(decoder, 2, __func__, "Preamble not found"); + decoder_log(decoder, 3, __func__, "Preamble not found"); return DECODE_ABORT_EARLY; } - if (pos + 7 * 8 > bitbuffer->bits_per_row[0]) { + if (pos + 8 * 8 > bitbuffer->bits_per_row[0]) { decoder_log(decoder, 2, __func__, "Length check fail"); return DECODE_ABORT_LENGTH; } - uint8_t b[6] = {0}; + uint8_t b[7] = {0}; uint8_t cc[1] = {0}; bitbuffer_extract_bytes(bitbuffer, 0, pos + 16, cc, sizeof(cc) * 8); bitbuffer_extract_bytes(bitbuffer, 0, pos + 24, b, sizeof(b) * 8); - // verify checksum - if ((add_bytes(b, 6) & 0xff) != cc[0]) { + + // Verify checksum + // If the checksum is greater than 0xFF then the MSB is set. + // It occurs whether the bit is already set or not and was observed when checksum was in the 0x1FF and the 0x2FF range. + int computed_checksum = add_bytes(b, sizeof(b)); + if (computed_checksum > 0xff) { + computed_checksum |= 0x80; + } + + if ((computed_checksum & 0xff) != cc[0]) { decoder_log(decoder, 2, __func__, "Checksum fail"); return DECODE_FAIL_MIC; } + + int temperature_C = b[4] - 50; + int flags1 = b[5]; + int flags2 = b[6]; + int fast_leak_detected = (flags1 & 0x10); // fast leak - reports every second + int infl_detected = (flags1 & 0x20) >> 5; // inflating - reports every 15 - 20 sec + + int fast_leak = fast_leak_detected && !infl_detected; + float pressure_kPa = (((flags2 & 0x01) << 8) + b[3]) * 2.5; + + // Low batt = 0x8000; + int low_batt = flags1 >> 7; // Low batt flag is MSB (activated at V < 3.15 V)(Device fails at V < 3.10 V) + // Mystery flag at (flags2 & 0x20) showed up during low batt testing + char id_str[7]; - sprintf(id_str, "%02x%02x%02x", b[0], b[1], b[2]); - float pressure_kPa = b[3] * 2.5; - int temperature_C = b[4] - 50; - int flags = b[5]; - char flags_str[3]; - sprintf(flags_str, "%x", flags); + snprintf(id_str, sizeof(id_str), "%02x%02x%02x", b[0], b[1], b[2]); + + char flags_str[5]; + snprintf(flags_str, sizeof(flags_str), "%02x%02x", flags1, flags2); + /* clang-format off */ data_t *data = data_make( - "model", "", DATA_STRING, "EezTire-E618", - "type", "", DATA_STRING, "TPMS", - "id", "", DATA_STRING, id_str, - "pressure_kPa", "Pressure", DATA_FORMAT, "%.0f kPa", DATA_DOUBLE, (double)pressure_kPa, - "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, (double)temperature_C, - "flags", "Flags", DATA_STRING, flags_str, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); + "model", "", DATA_STRING, "EezTire-E618", + "type", "", DATA_STRING, "TPMS", + "id", "", DATA_STRING, id_str, + "battery_ok", "Battery_OK", DATA_INT, !low_batt, + "pressure_kPa", "Pressure", DATA_FORMAT, "%.0f kPa", DATA_DOUBLE, (double)pressure_kPa, + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, (double)temperature_C, + "flags", "Flags", DATA_STRING, flags_str, + "fast_leak", "Fast Leak", DATA_INT, fast_leak, + "inflate", "Inflate", DATA_INT, infl_detected, + "mic", "Integrity", DATA_STRING, "CHECKSUM", + NULL); /* clang-format on */ decoder_output_data(decoder, data); @@ -98,15 +148,18 @@ static char const *const output_fields[] = { "model", "type", "id", - "temperature_C", + "battery_ok", "pressure_kPa", + "temperature_C", "flags", + "fast_leak", + "inflate", "mic", NULL, }; r_device const tpms_eezrv = { - .name = "EezTire E618 (TPMS10ATC)", + .name = "EezTire E618, Carchet TPMS", .modulation = OOK_PULSE_MANCHESTER_ZEROBIT, .short_width = 50, .long_width = 50, diff --git a/src/rtl_433/devices/tpms_elantra2012.c b/src/rtl_433/devices/tpms_elantra2012.c index efc50c2b..f7682942 100644 --- a/src/rtl_433/devices/tpms_elantra2012.c +++ b/src/rtl_433/devices/tpms_elantra2012.c @@ -51,13 +51,10 @@ Preamble is 111 0001 0101 0101 (0x7155). static int tpms_elantra2012_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; uint32_t id; - char id_str[9]; int flags; - char flags_str[3]; int pressure_kpa; int temperature_c; int triggered, battery_low, storage; @@ -73,21 +70,21 @@ static int tpms_elantra2012_decode(r_device *decoder, bitbuffer_t *bitbuffer, un return DECODE_FAIL_MIC; } - id = ((uint32_t)b[2] << 24) | (b[3] << 16) | (b[4] << 8) | (b[5]); - sprintf(id_str, "%08x", id); - - flags = b[6]; - sprintf(flags_str, "%x", flags); - + id = ((uint32_t)b[2] << 24) | (b[3] << 16) | (b[4] << 8) | (b[5]); + flags = b[6]; pressure_kpa = b[0] + 60; temperature_c = b[1] - 50; + storage = (b[6] & 0x04) >> 2; + battery_low = (b[6] & 0x02) >> 1; + triggered = (b[6] & 0x01) >> 0; - storage = (b[6] & 0x04) >> 2; - battery_low = (b[6] & 0x02) >> 1; - triggered = (b[6] & 0x01) >> 0; + char id_str[9]; + snprintf(id_str, sizeof(id_str), "%08x", id); + char flags_str[3]; + snprintf(flags_str, sizeof(flags_str), "%x", flags); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Elantra2012", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_ford.c b/src/rtl_433/devices/tpms_ford.c index 31cf2ec1..5f2e4476 100644 --- a/src/rtl_433/devices/tpms_ford.c +++ b/src/rtl_433/devices/tpms_ford.c @@ -55,13 +55,10 @@ Packet nibbles: static int tpms_ford_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; unsigned id; - char id_str[9]; int code; - char code_str[7], unknown_str[3], unknown_3_str[2]; float pressure_psi; int temperature_c, temperature_valid; int psibits; @@ -83,11 +80,9 @@ static int tpms_ford_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned } id = (unsigned)b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; - sprintf(id_str, "%08x", id); /* Extract and log code to aid in debugging. */ code = b[4] << 16 | b[5] << 8 | b[6]; - sprintf(code_str, "%06x", code); /* * Formula is a combination of regression and plausible, observed @@ -142,7 +137,7 @@ static int tpms_ford_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned * pattern. Therefore set all of them as the unknown * syndrome. */ - unknown |= 0x4c; + unknown = (b[6] & 0x4c); break; } @@ -153,14 +148,21 @@ static int tpms_ford_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned * so that leaves 0x80 and 0x10, which are expected to be 0. */ unknown |= (b[6] & 0x90); - sprintf(unknown_str, "%02x", unknown); /* Low-order 2 bits are variously 01, 10. */ unknown_3 = b[6] & 0x3; - sprintf(unknown_3_str, "%01x", unknown_3); + + char id_str[9]; + snprintf(id_str, sizeof(id_str), "%08x", id); + char code_str[7]; + snprintf(code_str, sizeof(code_str), "%06x", code); + char unknown_str[3]; + snprintf(unknown_str, sizeof(unknown_str), "%02x", unknown); + char unknown_3_str[2]; + snprintf(unknown_3_str, sizeof(unknown_3_str), "%01x", unknown_3); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Ford", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_hyundai_vdo.c b/src/rtl_433/devices/tpms_hyundai_vdo.c index ebfb9d71..7dd7672b 100644 --- a/src/rtl_433/devices/tpms_hyundai_vdo.c +++ b/src/rtl_433/devices/tpms_hyundai_vdo.c @@ -42,12 +42,10 @@ Packet nibbles: static int tpms_hyundai_vdo_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; int state; unsigned id; - char id_str[9 + 1]; int flags; int repeat; int pressure; @@ -80,10 +78,11 @@ static int tpms_hyundai_vdo_decode(r_device *decoder, bitbuffer_t *bitbuffer, un temperature = b[7]; maybe_battery = b[8]; - sprintf(id_str, "%08x", id); + char id_str[9 + 1]; + snprintf(id_str, sizeof(id_str), "%08x", id); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Hyundai-VDO", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_jansite.c b/src/rtl_433/devices/tpms_jansite.c index d723ea1a..b131aeb2 100644 --- a/src/rtl_433/devices/tpms_jansite.c +++ b/src/rtl_433/devices/tpms_jansite.c @@ -31,15 +31,12 @@ Data layout (nibbles): static int tpms_jansite_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; unsigned id; - char id_str[7 + 1]; int flags; int pressure; int temperature; - char code_str[7 * 2 + 1]; bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 56); @@ -56,11 +53,14 @@ static int tpms_jansite_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsign pressure = b[4]; temperature = b[5]; //crc = b[6]; - sprintf(id_str, "%07x", id); - sprintf(code_str, "%02x%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5], b[6]); // figure out the checksum + + char id_str[7 + 1]; + snprintf(id_str, sizeof(id_str), "%07x", id); + char code_str[7 * 2 + 1]; + snprintf(code_str, sizeof(code_str), "%02x%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5], b[6]); // figure out the checksum /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Jansite", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_jansite_solar.c b/src/rtl_433/devices/tpms_jansite_solar.c index c7d5393d..4dc5bba9 100644 --- a/src/rtl_433/devices/tpms_jansite_solar.c +++ b/src/rtl_433/devices/tpms_jansite_solar.c @@ -43,15 +43,12 @@ TODO: identify battery bits static int tpms_jansite_solar_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; unsigned id; - char id_str[7 + 1]; int flags; int pressure; int temperature; - char code_str[9 * 2 + 1]; bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 88); bitbuffer_invert(&packet_bits); @@ -77,11 +74,14 @@ static int tpms_jansite_solar_decode(r_device *decoder, bitbuffer_t *bitbuffer, flags = b[5]; temperature = b[6]; pressure = b[7]; - sprintf(id_str, "%06x", id); - sprintf(code_str, "%02x%02x%02x%02x%02x%02x%02x%02x%02x", b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10]); + + char id_str[7 + 1]; + snprintf(id_str, sizeof(id_str), "%06x", id); + char code_str[9 * 2 + 1]; + snprintf(code_str, sizeof(code_str), "%02x%02x%02x%02x%02x%02x%02x%02x%02x", b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10]); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Jansite-Solar", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_kia.c b/src/rtl_433/devices/tpms_kia.c index b36d4657..d5244927 100644 --- a/src/rtl_433/devices/tpms_kia.c +++ b/src/rtl_433/devices/tpms_kia.c @@ -26,7 +26,7 @@ identical. z: 16-bit preamble = 0xed71. Must be omitted from Manchester-decoding a: Unknown, but 0xf in all my own readings p: 8-bit pressure given as PSI * 5 - t: 8-bit temperature given as Celcius + 50 + t: 8-bit temperature given as Celsius + 50 i: 32-bit Sensor ID d: Unknown, with different value in each packet c: First 5 bits of CRC. We need to append 000 to reach 8 bits. poly=0x07, init=0x76. @@ -43,20 +43,15 @@ NOTE: You may need to use the "-s 1000000" option of rtl_433 in order to get a c static int tpms_kia_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; unsigned id; - char id_str[9 + 1]; - char unknown1_str[2 + 1]; - char unknown2_str[3 + 1]; uint8_t unknown1; uint8_t unknown2; uint8_t pressure; uint8_t temperature; uint8_t crc; - char raw[9 * 2 + 1]; // 9 bytes in hex notation unsigned int start_pos; const unsigned int preamble_length = 16; @@ -80,16 +75,20 @@ static int tpms_kia_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned r return DECODE_FAIL_MIC; } - sprintf(id_str, "%08x", id); - sprintf(unknown1_str, "%02x", unknown1); - sprintf(unknown2_str, "%03x", unknown2); - sprintf(raw, "%02x%02x%02x%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]); + char id_str[9 + 1]; + snprintf(id_str, sizeof(id_str), "%08x", id); + char unknown1_str[2 + 1]; + snprintf(unknown1_str, sizeof(unknown1_str), "%02x", unknown1); + char unknown2_str[3 + 1]; + snprintf(unknown2_str, sizeof(unknown2_str), "%03x", unknown2); + char raw[9 * 2 + 1]; // 9 bytes in hex notation + snprintf(raw, sizeof(raw), "%02x%02x%02x%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]); float pressure_float = pressure / 5.0; float temperature_float = temperature - 50.0; /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Kia", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_nissan.c b/src/rtl_433/devices/tpms_nissan.c new file mode 100644 index 00000000..c8808b70 --- /dev/null +++ b/src/rtl_433/devices/tpms_nissan.c @@ -0,0 +1,115 @@ +/** @file + Nissan FSK 37 bit Manchester encoded checksummed TPMS data. + Reference issue: https://github.com/merbanan/rtl_433/issues/1024 + + Copyright (C) 2021 Alex Wilson + + 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. +*/ +/** +Nissan FSK 37 bit Manchester encoded checksummed TPMS data. + +Data format: + + MODE:3d TPMS_ID:24h (PSI+THREE)*FOUR=8d UNKNOWN:2b +*/ + +#include "decoder.h" + +static int tpms_nissan_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) +{ + bitbuffer_t packet_bits = {0}; + + bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 113); + bitbuffer_invert(&packet_bits); // Manchester (G.E. Thomas) Decoded + + // FIXME Debug stuff + // fprintf(stderr, "packet_bits:\n"); + // bitbuffer_print(&packet_bits); + + // fprintf(stderr, "%s : bits %d\n", __func__, packet_bits.bits_per_row[0]); + if (packet_bits.bits_per_row[0] < 37) { + return DECODE_FAIL_SANITY; // sanity check failed + } + + uint8_t *b = packet_bits.bb[0]; + + // TODO Is there any parity or other checks we can perform to return + // DECODE_ABORT_EARLY or DECODE_FAIL_MIC + + // MODE:3d + int mode = b[0] >> 5; + + // TPMS_ID:24h + int id = (unsigned)((b[0] & 0x1F) << 19) | (b[1] << 11) | (b[2] << 3) | (b[3] >> 5); + + // (PSI+THREE)*FOUR=8d + int pressure_raw = ((b[3] & 0x1F) << 3) | (b[4] >> 5); + float pressure_psi = (double)(pressure_raw / 4.0) - 3.0; + + // UNKNOWN:2b + int unknown = (b[4] & 0x1F) >> 3; + + char id_str[7]; + snprintf(id_str, sizeof(id_str), "%06x", id); + + /* clang-format off */ + data_t *data = data_make( + "model", "", DATA_STRING, "Nissan", + "type", "", DATA_STRING, "TPMS", + "id", "", DATA_STRING, id_str, + "mode", "", DATA_INT, mode, + "pressure_psi", "Pressure", DATA_FORMAT, "%.1f PSI", DATA_DOUBLE, (double)(pressure_psi / 4.0) - 3.0, + "unknown", "", DATA_INT, unknown, + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; +} + +/** @sa tpms_nissan_decode() */ +static int tpms_nissan_callback(r_device *decoder, bitbuffer_t *bitbuffer) +{ + // preamble is f5 55 55 55 e + uint8_t const preamble_pattern[5] = {0xf5, 0x55, 0x55, 0x55, 0xe0}; // 36 bits + + unsigned bitpos = 0; + int ret = 0; + int events = 0; + + // Find a preamble with enough bits after it that it could be a complete packet + while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, preamble_pattern, 36)) + 77 <= + bitbuffer->bits_per_row[0]) { + ret = tpms_nissan_decode(decoder, bitbuffer, 0, bitpos + 36); + if (ret > 0) + events += ret; + bitpos += 1; + } + + return events > 0 ? events : ret; +} + +static char const *const output_fields[] = { + "model", + "type", + "id", + "mode", + "pressure_psi", + "unknown", + NULL, +}; + +r_device const tpms_nissan = { + .name = "Nissan TPMS", + .modulation = FSK_PULSE_PCM, + .short_width = 120, // TODO The preamble plus pre-MC data is 113, what should this be? + .long_width = 120, // FSK + .reset_limit = 250, // Maximum gap size before End Of Message [us]. TODO What should this be? + .decode_fn = &tpms_nissan_callback, + .disabled = 1, // no MIC, disabled by default + .fields = output_fields, +}; diff --git a/src/rtl_433/devices/tpms_pmv107j.c b/src/rtl_433/devices/tpms_pmv107j.c index 01419240..54122afe 100644 --- a/src/rtl_433/devices/tpms_pmv107j.c +++ b/src/rtl_433/devices/tpms_pmv107j.c @@ -65,7 +65,7 @@ static int tpms_pmv107j_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsign } char id_str[9]; - sprintf(id_str, "%08x", id); + snprintf(id_str, sizeof(id_str), "%08x", id); /* clang-format off */ data_t *data = data_make( diff --git a/src/rtl_433/devices/tpms_porsche.c b/src/rtl_433/devices/tpms_porsche.c index 643e9743..a44ee2cb 100644 --- a/src/rtl_433/devices/tpms_porsche.c +++ b/src/rtl_433/devices/tpms_porsche.c @@ -65,7 +65,7 @@ static int tpms_porsche_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsign int temperature_c = temperature - 40; char id_str[4 * 2 + 1]; - sprintf(id_str, "%08x", id); + snprintf(id_str, sizeof(id_str), "%08x", id); /* clang-format off */ data_t *data = data_make( diff --git a/src/rtl_433/devices/tpms_renault.c b/src/rtl_433/devices/tpms_renault.c index e226d7d5..4fe86327 100644 --- a/src/rtl_433/devices/tpms_renault.c +++ b/src/rtl_433/devices/tpms_renault.c @@ -28,16 +28,12 @@ Packet nibbles: static int tpms_renault_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; bitbuffer_t packet_bits = {0}; uint8_t *b; int flags; - char flags_str[3]; unsigned id; - char id_str[7]; int pressure_raw, temp_c, unknown; double pressure_kpa; - char code_str[5]; bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 160); // require 72 data bits @@ -51,20 +47,22 @@ static int tpms_renault_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsign return 0; } - flags = b[0] >> 2; - sprintf(flags_str, "%02x", flags); - - id = b[5] << 16 | b[4] << 8 | b[3]; // little-endian - sprintf(id_str, "%06x", id); - + flags = b[0] >> 2; + id = b[5] << 16 | b[4] << 8 | b[3]; // little-endian pressure_raw = (b[0] & 0x03) << 8 | b[1]; pressure_kpa = pressure_raw * 0.75; temp_c = b[2] - 30; unknown = b[7] << 8 | b[6]; // little-endian, fixed 0xffff? - sprintf(code_str, "%04x", unknown); + + char flags_str[3]; + snprintf(flags_str, sizeof(flags_str), "%02x", flags); + char id_str[7]; + snprintf(id_str, sizeof(id_str), "%06x", id); + char code_str[5]; + snprintf(code_str, sizeof(code_str), "%04x", unknown); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Renault", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_renault_0435r.c b/src/rtl_433/devices/tpms_renault_0435r.c index 23fb59b0..809fc5e0 100644 --- a/src/rtl_433/devices/tpms_renault_0435r.c +++ b/src/rtl_433/devices/tpms_renault_0435r.c @@ -102,17 +102,17 @@ static int tpms_renault_0435r_decode(r_device *decoder, bitbuffer_t *bitbuffer, // observed always 0xc0 - FIXME: find possible combinations and reject message with impossible combination // to avoid confusion with other FSK manchester 9-byte sensors with 8bit xor checksum. - char id_str[7]; - sprintf(id_str, "%02x%02x%02x", b[0], b[1], b[2]); - - char flags_str[3]; - sprintf(flags_str, "%02x", flags); - int pressure_raw = b[4]; double pressure_kpa = pressure_raw / 0.75; int temp_c = (int)b[5] - 50; int rad_acc = (int)b[6] * 5; + char id_str[7]; + snprintf(id_str, sizeof(id_str), "%02x%02x%02x", b[0], b[1], b[2]); + + char flags_str[3]; + snprintf(flags_str, sizeof(flags_str), "%02x", flags); + /* clang-format off */ data_t *data = data_make( "model", "", DATA_STRING, "Renault-0435R", diff --git a/src/rtl_433/devices/tpms_toyota.c b/src/rtl_433/devices/tpms_toyota.c index 92aa3f6a..d0234d01 100644 --- a/src/rtl_433/devices/tpms_toyota.c +++ b/src/rtl_433/devices/tpms_toyota.c @@ -30,12 +30,10 @@ The pressure seems to be 1/4 PSI offset by -7 PSI (i.e. 28 raw = 0 PSI). static int tpms_toyota_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { - data_t *data; unsigned int start_pos; bitbuffer_t packet_bits = {0}; uint8_t *b; unsigned id; - char id_str[9]; unsigned status, pressure1, pressure2, temp; int crc; @@ -62,10 +60,11 @@ static int tpms_toyota_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigne return 0; } - sprintf(id_str, "%08x", id); + char id_str[9]; + snprintf(id_str, sizeof(id_str), "%08x", id); /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "", DATA_STRING, "Toyota", "type", "", DATA_STRING, "TPMS", "id", "", DATA_STRING, id_str, diff --git a/src/rtl_433/devices/tpms_truck.c b/src/rtl_433/devices/tpms_truck.c index 7fb47bfd..e74f69b2 100644 --- a/src/rtl_433/devices/tpms_truck.c +++ b/src/rtl_433/devices/tpms_truck.c @@ -74,7 +74,7 @@ static int tpms_truck_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned int temperature = b[7]; char id_str[4 * 2 + 1]; - sprintf(id_str, "%08x", id); + snprintf(id_str, sizeof(id_str), "%08x", id); /* clang-format off */ data_t *data = data_make( diff --git a/src/rtl_433/devices/tpms_tyreguard400.c b/src/rtl_433/devices/tpms_tyreguard400.c index b393e5f3..472ca130 100644 --- a/src/rtl_433/devices/tpms_tyreguard400.c +++ b/src/rtl_433/devices/tpms_tyreguard400.c @@ -39,7 +39,7 @@ Packet layout: To peer a new sensor to the unit, bit 79 and 80 has to be both to 1. -NOTE: In the datasheet, it is said that the sensor can report low batterie. During my tests/reseach i'm not able to see this behavior. I have fuzzed all bits nothing was reported to the reader. +NOTE: In the datasheet, it is said that the sensor can report low batterie. During my tests/research i'm not able to see this behavior. I have fuzzed all bits nothing was reported to the reader. Flex decoder: @@ -95,9 +95,9 @@ static int tpms_tyreguard400_decode(r_device *decoder, bitbuffer_t *bitbuffer, u //int add1024 = (flags & 0x40) >> 6; // bytes = 0b0X000000 char id_str[8]; - sprintf(id_str, "%07x", (uint32_t)(((b[3] & 0xf)<<24)) | (b[4]<<16) | (b[5]<<8) | b[6]); // 28 bits ID + snprintf(id_str, sizeof(id_str), "%07x", (uint32_t)(((b[3] & 0xf)<<24)) | (b[4]<<16) | (b[5]<<8) | b[6]); // 28 bits ID char flags_str[3]; - sprintf(flags_str, "%02x", flags); + snprintf(flags_str, sizeof(flags_str), "%02x", flags); //id = (b[4] << 8) int pressure_kpa = b[7] | ((flags & 0x70) << 4); @@ -130,7 +130,7 @@ static int tpms_tyreguard400_decode(r_device *decoder, bitbuffer_t *bitbuffer, u static int tpms_tyreguard400_callback(r_device *decoder, bitbuffer_t *bitbuffer) { //uint8_t const tyreguard_frame_sync[] = {0xf, 0xd5, 0xfd, 0x5f} - uint8_t const tyreguard_frame_sync[] = {0xfd, 0x5f, 0xd5, 0xf0}; // needs to shift sync to align bytes 28x bits usefull + uint8_t const tyreguard_frame_sync[] = {0xfd, 0x5f, 0xd5, 0xf0}; // needs to shift sync to align bytes 28x bits useful int ret = 0; int events = 0; diff --git a/src/rtl_433/devices/ttx201.c b/src/rtl_433/devices/ttx201.c index ab6e778b..e669b0c6 100644 --- a/src/rtl_433/devices/ttx201.c +++ b/src/rtl_433/devices/ttx201.c @@ -12,6 +12,11 @@ Emos TTX201 Thermo Remote Sensor. Manufacturer: Ewig Industries Macao Maybe same as Ewig TTX201M (FCC ID: N9ZTTX201M) +IROX ETS69 temperature sensor with DCF77 receiver for EBR606C weather station (Ewig WSA101) +uses the same protocol. It transmits temperature the same way as TTX201 (except for different M bits). +If its internal clock is synchronized to DCF77, it transmits the date/time every hour (:00) instead of +the temperature. The date/time is also transmitted after clock is synced at startup. + Transmit Interval: every ~61 s Frequency: 433.92 MHz Manchester Encoding, pulse width: 500 us, interpacket gap width 1500 us. @@ -29,20 +34,32 @@ A complete message is 445 bits: 54-bit data packet format 0 1 2 3 4 5 6 7 8 9 10 11 12 13 (nibbles #, aligned to 8-bit values) - ..LL LLKKKKKK IIIIIIII S???BCCC ?XXXTTTT TTTTTTTT MMMMMMMM JJJJ + ..LL LLKKKKKK IIIIIIII StttBCCC 0XXXTTTT TTTTTTTT MMMMMMMM JJJJ (temperature) +or ..LL LLKKKKKK zyyyyyyy 0tttmmmm dddddHHH HHMMMMMM 0SSSSSS? JJJJ (date/time) - L = 4-bit start of packet, always 0 - K = 6-bit checksum, sum of nibbles 3-12 - I = 8-bit sensor ID - S = startup (0 = normal operation, 1 = reset or battery changed) -- ? = unknown, always 0 +- t = data type (000 = temperature, 101 = date/time) +- 0 = unknown, always 0 - B = battery status (0 = OK, 1 = low) - C = 3-bit channel, 0-4 - X = 3-bit packet index, 0-7 - T = 12-bit signed temperature * 10 in Celsius -- M = 8-bit postmark, always 0x14 +- M = 8-bit postmark (sensor model?), always 0x14 for TTX201, 0x00 for ETS69 - J = 4-bit packet separator, always 0xF +date/time bit definitions: +- z = time zone/summer time (0 = CET, 1 = CEST) +- y = year +- m = month +- d = day +- H = hour +- M = minute +- S = second +- ? = purpose unknown, always 0 or 1, changes only after reset (battery change) + Sample received raw data package: bitbuffer:: Number of rows: 10 @@ -82,6 +99,9 @@ Data decoded: #define MSG_PAD_BITS ((((MSG_PACKET_BITS / 8) + 1) * 8) - MSG_PACKET_BITS) #define MSG_PACKET_LEN ((MSG_PACKET_BITS + MSG_PAD_BITS) / 8) +#define DATA_TYPE_TEMP 0x00 +#define DATA_TYPE_DATETIME 0x05 + static int checksum_calculate(uint8_t *b) { int i; @@ -99,6 +119,7 @@ static int ttx201_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row int bits = bitbuffer->bits_per_row[row]; int checksum; int checksum_calculated; + int data_type; int postmark; int device_id; int battery_low; @@ -127,6 +148,7 @@ static int ttx201_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row /* Aligned data: LLKKKKKK IIIIIIII S???BCCC ?XXXTTTT TTTTTTTT MMMMMMMM JJJJ */ checksum = b[0] & 0x3f; checksum_calculated = checksum_calculate(b); + data_type = (b[2] & 0x70) >> 4; postmark = b[5]; if (decoder->verbose > 1) { @@ -151,33 +173,47 @@ static int ttx201_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row decoder_log(decoder, 0, __func__, ""); } - if (postmark != MSG_PACKET_POSTMARK) { - decoder_logf(decoder, 2, __func__, "Packet #%u wrong postmark 0x%02x (expected 0x%02x).", - row, postmark, MSG_PACKET_POSTMARK); - return DECODE_FAIL_SANITY; - } - if (checksum != checksum_calculated) { decoder_logf(decoder, 2, __func__, "Packet #%u checksum error.", row); return DECODE_FAIL_MIC; } - device_id = b[1]; - battery_low = (b[2] & 0x08) != 0; // if not zero, battery is low - channel = (b[2] & 0x07) + 1; - temperature = (int16_t)(((b[3] & 0x0f) << 12) | (b[4] << 4)); // uses sign extend - temperature_c = (temperature >> 4) * 0.1f; - - /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "Emos-TTX201", - "id", "House Code", DATA_INT, device_id, - "channel", "Channel", DATA_INT, channel, - "battery_ok", "Battery", DATA_INT, !battery_low, - "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature_c, - "mic", "Integrity", DATA_STRING, "CHECKSUM", - NULL); - /* clang-format on */ + if (data_type == DATA_TYPE_DATETIME) { + int cest = b[1] & 0x80; + int year = b[1] & 0x7f; + int month = b[2] & 0x0f; + int day = (b[3] & 0xf8) >> 3; + int hour = (b[3] & 0x07) << 2 | (b[4] & 0xc0) >> 6; + int minute = b[4] & 0x3f; + int second = (b[5] & 0x7e) >> 1; + char clock_str[25]; + snprintf(clock_str, sizeof(clock_str), "%04d-%02d-%02dT%02d:%02d:%02d %s", year + 2000, month, day, hour, minute, second, cest ? "CEST" : "CET"); + + /* clang-format off */ + data = data_make( + "model", "", DATA_STRING, "Emos-TTX201", + "radio_clock", "Radio Clock", DATA_STRING, clock_str, + "mic", "Integrity", DATA_STRING, "CHECKSUM", + NULL); + /* clang-format on */ + } else { // temperature + device_id = b[1]; + battery_low = (b[2] & 0x08) != 0; // if not zero, battery is low + channel = (b[2] & 0x07) + 1; + temperature = (int16_t)(((b[3] & 0x0f) << 12) | (b[4] << 4)); // uses sign extend + temperature_c = (temperature >> 4) * 0.1f; + + /* clang-format off */ + data = data_make( + "model", "", DATA_STRING, "Emos-TTX201", + "id", "House Code", DATA_INT, device_id, + "channel", "Channel", DATA_INT, channel, + "battery_ok", "Battery", DATA_INT, !battery_low, + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature_c, + "mic", "Integrity", DATA_STRING, "CHECKSUM", + NULL); + /* clang-format on */ + } decoder_output_data(decoder, data); return 1; @@ -213,6 +249,7 @@ static char const *const output_fields[] = { "battery_ok", "temperature_C", "mic", + "radio_clock", NULL, }; diff --git a/src/rtl_433/devices/visonic_powercode.c b/src/rtl_433/devices/visonic_powercode.c index a9433b8d..bc20b729 100644 --- a/src/rtl_433/devices/visonic_powercode.c +++ b/src/rtl_433/devices/visonic_powercode.c @@ -49,8 +49,6 @@ Protocol cribbed from: static int visonic_powercode_decode(r_device *decoder, bitbuffer_t *bitbuffer) { uint8_t msg[32]; - data_t *data; - char id[7]; uint8_t lrc; // 37 bits expected, 6 packet repetitions, accept 4 @@ -81,13 +79,13 @@ static int visonic_powercode_decode(r_device *decoder, bitbuffer_t *bitbuffer) decoder_logf(decoder, 2, __func__, "data byte is %02x", msg[3]); // format device id - sprintf(id, "%02x%02x%02x", msg[0], msg[1], msg[2]); + char id_str[7]; + snprintf(id_str, sizeof(id_str), "%02x%02x%02x", msg[0], msg[1], msg[2]); - // populate data byte fields /* clang-format off */ - data = data_make( + data_t *data = data_make( "model", "Model", DATA_STRING, "Visonic-Powercode", - "id", "ID", DATA_STRING, id, + "id", "ID", DATA_STRING, id_str, "tamper", "Tamper", DATA_INT, ((0x80 & msg[3]) == 0x80) ? 1 : 0, "alarm", "Alarm", DATA_INT, ((0x40 & msg[3]) == 0x40) ? 1 : 0, "battery_ok", "Battery", DATA_INT, ((0x20 & msg[3]) == 0x20) ? 0 : 1, diff --git a/src/rtl_433/devices/wec2103.c b/src/rtl_433/devices/wec2103.c index 7d6bfd31..aa0ca6b8 100644 --- a/src/rtl_433/devices/wec2103.c +++ b/src/rtl_433/devices/wec2103.c @@ -14,6 +14,8 @@ /** WEC-2103 temperature/humidity sensor. +Circuit board model numbers: TX07Y-THC V1, TX07K-THC V4 + Similar to prologue, kedsum, esperanza_ews, s3318p Only available information for this device: https://fcc.report/FCC-ID/WEC-2103 @@ -21,19 +23,19 @@ Only available information for this device: https://fcc.report/FCC-ID/WEC-2103 Byte: 0 1 2 3 4 5 Nibble: 1 2 3 4 5 6 7 8 9 10 11 - Type: IIIIIIII XXXXFFFF TTTTTTTT TTTTHHHH HHHHCCCC ???? + Type: IIIIIIII XXXXFFFF TTTTTTTT TTTTHHHH HHHHCCCC SS - I: random device ID, changes on powercycle -- X: Checksum? -- F: Flags +- X: Checksum: mangled CRC-4, poly 3, init 0. +- F: Flags: tx-button pressed|batt-low|?|? - T: Temperature - H: Humidity -- Flags: tx-button pressed|?|?|? +- S: Stop bit(s): 0b10 Example datagram: - f2 90 6b5 96 1 8 - |ID|Checksum?+Flags|Temperature|Humidity|Channel|unknown + f2 90 6b5 96 1 8 + |ID|Checksum+Flags|Temperature|Humidity|Channel|Stop bits - Temperature in Fahrenheit*100+900->hex - Example: 82.4F->824->1724->0x6bc @@ -41,31 +43,41 @@ Example datagram: static int wec2103_decode(r_device *decoder, bitbuffer_t *bitbuffer) { - if (bitbuffer->num_rows != 6 || bitbuffer->bits_per_row[2] != 42) + if (bitbuffer->num_rows != 6 || bitbuffer->bits_per_row[2] != 42) { return DECODE_ABORT_LENGTH; - - uint8_t b[6]; - bitbuffer_extract_bytes(bitbuffer, 3, 0, b, 42); - - int temp_raw = (b[2] << 4) | ((b[3] & 0xf0) >> 4); - int device_id = b[0]; - int channel = b[4] & 0x0f; - int flags = b[1] & 0xf; - float temp_f = (temp_raw - 900) * 0.1f; - int humidity = ((b[3] & 0x0f) * 10) + ((b[4] & 0xf0) >> 4); - int button = (b[1] & 0x08) >> 3; + } + + uint8_t b[5]; + bitbuffer_extract_bytes(bitbuffer, 3, 0, b, 40); + + int crc_received = b[1] >> 4; + b[1] = (b[1] & 0x0F) | ((b[4] & 0x0f) << 4); + int crc_calculated = crc4(b, sizeof(b) - 1, 3, 0) ^ (b[4] >> 4); + if (crc_calculated != crc_received) { + decoder_logf(decoder, 0, __func__, "CRC check failed (0x%X != 0x%X)", crc_calculated, crc_received); + return DECODE_FAIL_MIC; + } + + int temp_raw = (b[2] << 4) | ((b[3] & 0xf0) >> 4); + int device_id = b[0]; + int channel = b[4] & 0x0f; + int flags = b[1] & 0xf; + float temp_f = (temp_raw - 900) * 0.1f; + int humidity = ((b[3] & 0x0f) * 10) + ((b[4] & 0xf0) >> 4); + int button = (b[1] & 0x08) >> 3; + int battery_low = (b[1] & 0x04) >> 3; /* clang-format off */ data_t *data = data_make( "model", "", DATA_STRING, "WEC-2103", "id", "ID", DATA_INT, device_id, "channel", "Channel", DATA_INT, channel, - //"battery_ok", "Battery", DATA_INT, !battery_low, - "flags", "Flags", DATA_INT, flags, + "battery_ok", "Battery", DATA_INT, !battery_low, + "button", "Button", DATA_INT, button, "temperature_F", "Temperature", DATA_FORMAT, "%.02f F", DATA_DOUBLE, temp_f, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, - "button", "Button", DATA_INT, button, - //"mic", "Integrity", DATA_STRING, "CRC", + "flags", "Flags", DATA_INT, flags, + "mic", "Integrity", DATA_STRING, "CRC", NULL); /* clang-format on */ @@ -77,9 +89,12 @@ static char const *const output_fields[] = { "model", "id", "channel", - "flags", + "battery_ok", + "button", "temperature_F", "humidity", + "flags", + "mic", NULL, }; @@ -91,6 +106,5 @@ r_device const wec2103 = { .gap_limit = 4400, .reset_limit = 9400, .decode_fn = &wec2103_decode, - .disabled = 1, // no checksum .fields = output_fields, }; diff --git a/src/rtl_433/devices/ws2032.c b/src/rtl_433/devices/ws2032.c index c8db4825..4f4788e0 100644 --- a/src/rtl_433/devices/ws2032.c +++ b/src/rtl_433/devices/ws2032.c @@ -83,7 +83,7 @@ static int fineoffset_ws2032_decode(r_device *decoder, bitbuffer_t *bitbuffer) /* clang-format off */ data = data_make( "model", "", DATA_STRING, "WS2032", - "id", "StationID", DATA_FORMAT, "%04X", DATA_INT, device_id, + "id", "Station ID", DATA_FORMAT, "%04X", DATA_INT, device_id, "battery_ok", "Battery", DATA_INT, !battery_low, "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, diff --git a/src/rtl_433/devices/x10_sec.c b/src/rtl_433/devices/x10_sec.c index 7697e978..9ed7e9bf 100644 --- a/src/rtl_433/devices/x10_sec.c +++ b/src/rtl_433/devices/x10_sec.c @@ -62,8 +62,6 @@ static int x10_sec_callback(r_device *decoder, bitbuffer_t *bitbuffer) data_t *data; uint8_t *b; /* bits of a row */ char const *event_str = "UNKNOWN"; /* human-readable event */ - char x10_id_str[12] = ""; /* string showing hex value */ - char x10_code_str[5] = ""; /* string showing hex value */ int battery_low = 0; /* battery indicator */ int delay = 0; /* delay setting */ uint8_t tamper = 0; /* tamper alarm indicator */ @@ -162,8 +160,10 @@ static int x10_sec_callback(r_device *decoder, bitbuffer_t *bitbuffer) } /* get x10_id_str, x10_code_str ready for output */ - sprintf(x10_id_str, "%02x%02x", b[0], b[4]); - sprintf(x10_code_str, "%02x", b[2]); + char x10_id_str[12]; + snprintf(x10_id_str, sizeof(x10_id_str), "%02x%02x", b[0], b[4]); + char x10_code_str[5]; + snprintf(x10_code_str, sizeof(x10_code_str), "%02x", b[2]); /* debug output */ decoder_logf_bitbuffer(decoder, 1, __func__, bitbuffer, "id=%02x%02x code=%02x event_str=%s", b[0], b[4], b[2], event_str); diff --git a/src/rtl_433/list.c b/src/rtl_433/list.c index 03701112..f56794e6 100644 --- a/src/rtl_433/list.c +++ b/src/rtl_433/list.c @@ -17,10 +17,12 @@ void list_ensure_size(list_t *list, size_t min_size) { if (!list->elems || list->size < min_size) { - list->elems = realloc(list->elems, min_size * sizeof(*list->elems)); - if (!list->elems) { + // the input pointer is still valid if reallocation fails + void *elems_realloc = realloc(list->elems, min_size * sizeof(*list->elems)); + if (!elems_realloc) { FATAL_REALLOC("list_ensure_size()"); } + list->elems = elems_realloc; list->size = min_size; list->elems[list->len] = NULL; // ensure a terminating NULL diff --git a/src/rtl_433/output_log.c b/src/rtl_433/output_log.c index e5e2df66..0973e2a8 100644 --- a/src/rtl_433/output_log.c +++ b/src/rtl_433/output_log.c @@ -164,5 +164,5 @@ struct data_output *data_output_log_create(int log_level, FILE *file) log->output.output_free = data_output_log_free; log->file = file; - return &log->output; + return (struct data_output *)log; } diff --git a/src/rtl_433/pulse_analyzer.c b/src/rtl_433/pulse_analyzer.c index 93feb5bf..84ad288c 100644 --- a/src/rtl_433/pulse_analyzer.c +++ b/src/rtl_433/pulse_analyzer.c @@ -202,7 +202,7 @@ static void hexstr_print(hexstr_t *h, FILE *out) #define TOLERANCE (0.2f) // 20% tolerance should still discern between the pulse widths: 0.33, 0.66, 1.0 /// Analyze the statistics of a pulse data structure and print result -void pulse_analyzer(pulse_data_t *data, int package_type) +void pulse_analyzer(pulse_data_t *data, int package_type, r_device* device) { if (data->num_pulses == 0) { fprintf(stderr, "No pulses detected.\n"); @@ -260,7 +260,8 @@ void pulse_analyzer(pulse_data_t *data, int package_type) // (float)data->fsk_f2_est / INT16_MAX * data->sample_rate / 2.0 / 1000.0); fprintf(stderr, "Guessing modulation: "); - r_device device = {.name = "Analyzer Device", 0}; + device->name = "Analyzer Device", + device->verbose = 2, histogram_sort_mean(&hist_pulses); // Easier to work with sorted data histogram_sort_mean(&hist_gaps); if (hist_pulses.bins[0].mean == 0) { @@ -276,43 +277,43 @@ void pulse_analyzer(pulse_data_t *data, int package_type) } else if (hist_pulses.bins_count == 1 && hist_gaps.bins_count > 1) { fprintf(stderr, "Pulse Position Modulation with fixed pulse width\n"); - device.modulation = OOK_PULSE_PPM; // TODO: there is not FSK_PULSE_PPM - device.short_width = to_us * hist_gaps.bins[0].mean; - device.long_width = to_us * hist_gaps.bins[1].mean; - device.gap_limit = to_us * (hist_gaps.bins[1].max + 1); // Set limit above next lower gap - device.reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap + device->modulation = OOK_PULSE_PPM; // TODO: there is not FSK_PULSE_PPM + device->short_width = to_us * hist_gaps.bins[0].mean; + device->long_width = to_us * hist_gaps.bins[1].mean; + device->gap_limit = to_us * (hist_gaps.bins[1].max + 1); // Set limit above next lower gap + device->reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap } else if (hist_pulses.bins_count == 2 && hist_gaps.bins_count == 1) { fprintf(stderr, "Pulse Width Modulation with fixed gap\n"); - device.modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PWM : OOK_PULSE_PWM; - device.short_width = to_us * hist_pulses.bins[0].mean; - device.long_width = to_us * hist_pulses.bins[1].mean; - device.tolerance = (device.long_width - device.short_width) * 0.4; - device.reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap + device->modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PWM : OOK_PULSE_PWM; + device->short_width = to_us * hist_pulses.bins[0].mean; + device->long_width = to_us * hist_pulses.bins[1].mean; + device->tolerance = (device->long_width - device->short_width) * 0.4; + device->reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap } else if (hist_pulses.bins_count == 2 && hist_gaps.bins_count == 2 && hist_periods.bins_count == 1) { fprintf(stderr, "Pulse Width Modulation with fixed period\n"); - device.modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PWM : OOK_PULSE_PWM; - device.short_width = to_us * hist_pulses.bins[0].mean; - device.long_width = to_us * hist_pulses.bins[1].mean; - device.tolerance = (device.long_width - device.short_width) * 0.4; - device.reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap + device->modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PWM : OOK_PULSE_PWM; + device->short_width = to_us * hist_pulses.bins[0].mean; + device->long_width = to_us * hist_pulses.bins[1].mean; + device->tolerance = (device->long_width - device->short_width) * 0.4; + device->reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap } else if (hist_pulses.bins_count == 2 && hist_gaps.bins_count == 2 && hist_periods.bins_count == 3) { fprintf(stderr, "Manchester coding\n"); - device.modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_MANCHESTER_ZEROBIT : OOK_PULSE_MANCHESTER_ZEROBIT; - device.short_width = to_us * MIN(hist_pulses.bins[0].mean, hist_pulses.bins[1].mean); // Assume shortest pulse is half period - device.long_width = 0; // Not used - device.reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap + device->modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_MANCHESTER_ZEROBIT : OOK_PULSE_MANCHESTER_ZEROBIT; + device->short_width = to_us * MIN(hist_pulses.bins[0].mean, hist_pulses.bins[1].mean); // Assume shortest pulse is half period + device->long_width = 0; // Not used + device->reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap } else if (hist_pulses.bins_count == 2 && hist_gaps.bins_count >= 3) { fprintf(stderr, "Pulse Width Modulation with multiple packets\n"); - device.modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PWM : OOK_PULSE_PWM; - device.short_width = to_us * hist_pulses.bins[0].mean; - device.long_width = to_us * hist_pulses.bins[1].mean; - device.gap_limit = to_us * (hist_gaps.bins[1].max + 1); // Set limit above second gap - device.tolerance = (device.long_width - device.short_width) * 0.4; - device.reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap + device->modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PWM : OOK_PULSE_PWM; + device->short_width = to_us * hist_pulses.bins[0].mean; + device->long_width = to_us * hist_pulses.bins[1].mean; + device->gap_limit = to_us * (hist_gaps.bins[1].max + 1); // Set limit above second gap + device->tolerance = (device->long_width - device->short_width) * 0.4; + device->reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap } else if ((hist_pulses.bins_count >= 3 && hist_gaps.bins_count >= 3) && (abs(hist_pulses.bins[1].mean - 2*hist_pulses.bins[0].mean) <= hist_pulses.bins[0].mean/8) // Pulses are multiples of shortest pulse @@ -321,10 +322,10 @@ void pulse_analyzer(pulse_data_t *data, int package_type) && (abs(hist_gaps.bins[1].mean - 2*hist_pulses.bins[0].mean) <= hist_pulses.bins[0].mean/8) && (abs(hist_gaps.bins[2].mean - 3*hist_pulses.bins[0].mean) <= hist_pulses.bins[0].mean/8)) { fprintf(stderr, "Non Return to Zero coding (Pulse Code)\n"); - device.modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PCM : OOK_PULSE_PCM; - device.short_width = to_us * hist_pulses.bins[0].mean; // Shortest pulse is bit width - device.long_width = to_us * hist_pulses.bins[0].mean; // Bit period equal to pulse length (NRZ) - device.reset_limit = to_us * hist_pulses.bins[0].mean * 1024; // No limit to run of zeros... + device->modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PCM : OOK_PULSE_PCM; + device->short_width = to_us * hist_pulses.bins[0].mean; // Shortest pulse is bit width + device->long_width = to_us * hist_pulses.bins[0].mean; // Bit period equal to pulse length (NRZ) + device->reset_limit = to_us * hist_pulses.bins[0].mean * 1024; // No limit to run of zeros... } else if (hist_pulses.bins_count == 3) { fprintf(stderr, "Pulse Width Modulation with sync/delimiter\n"); @@ -332,11 +333,11 @@ void pulse_analyzer(pulse_data_t *data, int package_type) histogram_sort_count(&hist_pulses); int p1 = hist_pulses.bins[1].mean; int p2 = hist_pulses.bins[2].mean; - device.modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PWM : OOK_PULSE_PWM; - device.short_width = to_us * (p1 < p2 ? p1 : p2); // Set to shorter pulse width - device.long_width = to_us * (p1 < p2 ? p2 : p1); // Set to longer pulse width - device.sync_width = to_us * hist_pulses.bins[0].mean; // Set to lowest count pulse width - device.reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap + device->modulation = (package_type == PULSE_DATA_FSK) ? FSK_PULSE_PWM : OOK_PULSE_PWM; + device->short_width = to_us * (p1 < p2 ? p1 : p2); // Set to shorter pulse width + device->long_width = to_us * (p1 < p2 ? p2 : p1); // Set to longer pulse width + device->sync_width = to_us * hist_pulses.bins[0].mean; // Set to lowest count pulse width + device->reset_limit = to_us * (hist_gaps.bins[hist_gaps.bins_count - 1].max + 1); // Set limit above biggest gap } else { fprintf(stderr, "No clue...\n"); @@ -425,42 +426,42 @@ void pulse_analyzer(pulse_data_t *data, int package_type) } // Demodulate (if detected) - if (device.modulation) { + if (device->modulation) { fprintf(stderr, "Attempting demodulation... short_width: %.0f, long_width: %.0f, reset_limit: %.0f, sync_width: %.0f\n", - device.short_width, device.long_width, - device.reset_limit, device.sync_width); - switch (device.modulation) { + device->short_width, device->long_width, + device->reset_limit, device->sync_width); + switch (device->modulation) { case FSK_PULSE_PCM: fprintf(stderr, "Use a flex decoder with -X 'n=name,m=FSK_PCM,s=%.0f,l=%.0f,r=%.0f'\n", - device.short_width, device.long_width, device.reset_limit); - pulse_slicer_pcm(data, &device); + device->short_width, device->long_width, device->reset_limit); + pulse_slicer_pcm(data, device); break; case OOK_PULSE_PPM: fprintf(stderr, "Use a flex decoder with -X 'n=name,m=OOK_PPM,s=%.0f,l=%.0f,g=%.0f,r=%.0f'\n", - device.short_width, device.long_width, - device.gap_limit, device.reset_limit); - data->gap[data->num_pulses - 1] = device.reset_limit / to_us + 1; // Be sure to terminate package - pulse_slicer_ppm(data, &device); + device->short_width, device->long_width, + device->gap_limit, device->reset_limit); + data->gap[data->num_pulses - 1] = device->reset_limit / to_us + 1; // Be sure to terminate package + pulse_slicer_ppm(data, device); break; case OOK_PULSE_PWM: fprintf(stderr, "Use a flex decoder with -X 'n=name,m=OOK_PWM,s=%.0f,l=%.0f,r=%.0f,g=%.0f,t=%.0f,y=%.0f'\n", - device.short_width, device.long_width, device.reset_limit, - device.gap_limit, device.tolerance, device.sync_width); - data->gap[data->num_pulses - 1] = device.reset_limit / to_us + 1; // Be sure to terminate package - pulse_slicer_pwm(data, &device); + device->short_width, device->long_width, device->reset_limit, + device->gap_limit, device->tolerance, device->sync_width); + data->gap[data->num_pulses - 1] = device->reset_limit / to_us + 1; // Be sure to terminate package + pulse_slicer_pwm(data, device); break; case FSK_PULSE_PWM: fprintf(stderr, "Use a flex decoder with -X 'n=name,m=FSK_PWM,s=%.0f,l=%.0f,r=%.0f,g=%.0f,t=%.0f,y=%.0f'\n", - device.short_width, device.long_width, device.reset_limit, - device.gap_limit, device.tolerance, device.sync_width); - data->gap[data->num_pulses - 1] = device.reset_limit / to_us + 1; // Be sure to terminate package - pulse_slicer_pwm(data, &device); + device->short_width, device->long_width, device->reset_limit, + device->gap_limit, device->tolerance, device->sync_width); + data->gap[data->num_pulses - 1] = device->reset_limit / to_us + 1; // Be sure to terminate package + pulse_slicer_pwm(data, device); break; case OOK_PULSE_MANCHESTER_ZEROBIT: fprintf(stderr, "Use a flex decoder with -X 'n=name,m=OOK_MC_ZEROBIT,s=%.0f,l=%.0f,r=%.0f'\n", - device.short_width, device.long_width, device.reset_limit); - data->gap[data->num_pulses - 1] = device.reset_limit / to_us + 1; // Be sure to terminate package - pulse_slicer_manchester_zerobit(data, &device); + device->short_width, device->long_width, device->reset_limit); + data->gap[data->num_pulses - 1] = device->reset_limit / to_us + 1; // Be sure to terminate package + pulse_slicer_manchester_zerobit(data, device); break; default: fprintf(stderr, "Unsupported\n"); diff --git a/src/rtl_433/pulse_data.c b/src/rtl_433/pulse_data.c index f65cde2e..2531afd1 100644 --- a/src/rtl_433/pulse_data.c +++ b/src/rtl_433/pulse_data.c @@ -13,6 +13,7 @@ #include "pulse_data.h" #include "rfraw.h" #include "r_util.h" +#include "fatal.h" #include #include #include @@ -74,6 +75,10 @@ static inline void chk_ret(int ret) void pulse_data_print_vcd_header(FILE *file, uint32_t sample_rate) { + if (!file) { + FATAL("Invalid stream in pulse_data_print_vcd_header()"); + } + char time_str[LOCAL_TIME_BUFLEN]; char *timescale; if (sample_rate <= 500000) @@ -161,6 +166,10 @@ void pulse_data_load(FILE *file, pulse_data_t *data, uint32_t sample_rate) void pulse_data_print_pulse_header(FILE *file) { + if (!file) { + FATAL("Invalid stream in pulse_data_print_pulse_header()"); + } + char time_str[LOCAL_TIME_BUFLEN]; chk_ret(fprintf(file, ";pulse data\n")); @@ -172,6 +181,10 @@ void pulse_data_print_pulse_header(FILE *file) void pulse_data_dump(FILE *file, pulse_data_t const *data) { + if (!file) { + FATAL("Invalid stream in pulse_data_dump()"); + } + char time_str[LOCAL_TIME_BUFLEN]; chk_ret(fprintf(file, ";received %s\n", format_time_str(time_str, NULL, 1, 0))); diff --git a/src/rtl_433/pulse_slicer.c b/src/rtl_433/pulse_slicer.c index 6a1f1597..421c2f80 100644 --- a/src/rtl_433/pulse_slicer.c +++ b/src/rtl_433/pulse_slicer.c @@ -55,6 +55,7 @@ static int account_event(r_device* device, bitbuffer_t* bits, char const* demod_ } } + // Debug printout if (!device->decode_fn || (device->verbose && ret > 0) || (device->verbose > 1 && max_bits > 16) || (device->verbose > 2)) { decoder_log_bitbuffer(device, ret > 0 ? 1 : 2, demod_name, bits, device->name); } @@ -82,11 +83,12 @@ int pulse_slicer_pcm(pulse_data_t const* pulses, r_device* device) { float f_long = device->long_width > 0.0 ? 1.0 / (device->long_width * samples_per_us) : 0; int events = 0; - // bitbuffer_t bits = {0}; bitbuffer_clear(&bits); int const gap_limit = s_gap ? s_gap : s_reset; int const max_zeros = gap_limit / s_long; + if (s_tolerance <= 0) + s_tolerance = s_long / 4; // default tolerance is +-25% of a bit period // if there is a run of bit-wide toggles (preamble) tune the bit period int min_count = s_short == s_long ? 12 : 4; @@ -253,7 +255,6 @@ int pulse_slicer_ppm(pulse_data_t const* pulses, r_device* device) { } int events = 0; - // bitbuffer_t bits = {0}; bitbuffer_clear(&bits); // lower and upper bounds (non inclusive) @@ -317,9 +318,6 @@ int pulse_slicer_pwm(pulse_data_t const* pulses, r_device* device) { int s_sync = device->sync_width * samples_per_us; int s_tolerance = device->tolerance * samples_per_us; -// if (s_tolerance <= 0) // From https://github.com/NorthernMan54/rtl_433_ESP/pull/65 -// s_tolerance = s_long / 4; // default tolerance is +-25% of a bit period - // check for rounding to zero if ((device->short_width > 0 && s_short <= 0) || (device->long_width > 0 && s_long <= 0) || (device->reset_limit > 0 && s_reset <= 0) || (device->gap_limit > 0 && s_gap <= 0) || (device->sync_width > 0 && s_sync <= 0) || (device->tolerance > 0 && s_tolerance <= 0)) { print_logf(LOG_WARNING, __func__, "sample rate too low for protocol %u \"%s\"", device->protocol_num, device->name); @@ -327,7 +325,6 @@ int pulse_slicer_pwm(pulse_data_t const* pulses, r_device* device) { } int events = 0; - // bitbuffer_t bits = {0}; bitbuffer_clear(&bits); // lower and upper bounds (non inclusive) @@ -426,7 +423,6 @@ int pulse_slicer_manchester_zerobit(pulse_data_t const* pulses, r_device* device int events = 0; int time_since_last = 0; - // bitbuffer_t bits = {0}; bitbuffer_clear(&bits); // First rising edge is always counted as a zero (Seems to be hardcoded policy for the Oregon Scientific sensors...) @@ -498,7 +494,6 @@ int pulse_slicer_dmc(pulse_data_t const* pulses, r_device* device) { return 0; } - // bitbuffer_t bits = {0}; bitbuffer_clear(&bits); int events = 0; @@ -508,7 +503,7 @@ int pulse_slicer_dmc(pulse_data_t const* pulses, r_device* device) { if (abs(symbol - s_short) < s_tolerance) { // Short - 1 bitbuffer_add_bit(&bits, 1); - symbol = pulse_slicer_get_symbol(pulses, ++n); + symbol = n + 1 < pulses->num_pulses * 2 ? pulse_slicer_get_symbol(pulses, ++n) : 0; if (abs(symbol - s_short) > s_tolerance) { if (symbol >= s_reset - s_tolerance) { // Don't expect another short gap at end of message @@ -554,7 +549,6 @@ int pulse_slicer_piwm_raw(pulse_data_t const* pulses, r_device* device) { int w; - // bitbuffer_t bits = {0}; bitbuffer_clear(&bits); int events = 0; @@ -602,7 +596,6 @@ int pulse_slicer_piwm_dc(pulse_data_t const* pulses, r_device* device) { return 0; } - // bitbuffer_t bits = {0}; bitbuffer_clear(&bits); int events = 0; @@ -650,7 +643,6 @@ int pulse_slicer_nrzs(pulse_data_t const* pulses, r_device* device) { } int events = 0; - // bitbuffer_t bits = {0}; bitbuffer_clear(&bits); int limit = s_short; @@ -705,7 +697,6 @@ int pulse_slicer_osv1(pulse_data_t const* pulses, r_device* device) { int preamble = 0; int events = 0; int manbit = 0; - // bitbuffer_t bits = {0}; bitbuffer_clear(&bits); int halfbit_min = s_short / 2; int halfbit_max = s_short * 3 / 2; @@ -770,7 +761,7 @@ int pulse_slicer_osv1(pulse_data_t const* pulses, r_device* device) { int pulse_slicer_string(const char* code, r_device* device) { int events = 0; - bitbuffer_t bits = {0}; + bitbuffer_clear(&bits); bitbuffer_parse(&bits, code); diff --git a/src/rtl_433/r_api.c b/src/rtl_433/r_api.c index ec8c255d..bedc44a6 100644 --- a/src/rtl_433/r_api.c +++ b/src/rtl_433/r_api.c @@ -1,6 +1,5 @@ /** @file - Generic RF data receiver and decoder for ISM band devices using RTL-SDR and - SoapySDR. + Generic RF data receiver and decoder for ISM band devices using RTL-SDR and SoapySDR. Copyright (C) 2019 Christian W. Zuckschwerdt @@ -19,13 +18,6 @@ #include #include -#include "pulse_slicer.h" -#include "r_device.h" -#include "r_private.h" -#include "r_util.h" -#include "rtl_433.h" -#include "rtl_433_devices.h" -// #include "pulse_detect_fsk.h" // #include "compat_time.h" #include "data.h" // #include "data_tag.h" @@ -42,8 +34,18 @@ // #include "output_rtltcp.h" // #include "output_trigger.h" // #include "output_udp.h" +#include "pulse_analyzer.h" +// #include "pulse_detect_fsk.h" +#include "pulse_slicer.h" +#include "r_device.h" +#include "r_private.h" +#include "r_util.h" +#include "rtl_433.h" +#include "rtl_433_devices.h" // #include "sdr.h" // #include "write_sigrok.h" + +//TODO:rtl_433_ESP #include "log.h" #ifdef _WIN32 @@ -96,7 +98,7 @@ char const* version_string(void) { /* helper */ /* -struct mg_mgr *get_mgr(r_cfg_t *cfg) { +struct mg_mgr* get_mgr(r_cfg_t* cfg) { if (!cfg->mgr) { cfg->mgr = calloc(1, sizeof(*cfg->mgr)); if (!cfg->mgr) @@ -107,25 +109,25 @@ struct mg_mgr *get_mgr(r_cfg_t *cfg) { return cfg->mgr; } -void set_center_freq(r_cfg_t *cfg, uint32_t center_freq) { +void set_center_freq(r_cfg_t* cfg, uint32_t center_freq) { cfg->frequencies = 1; cfg->frequency_index = 0; cfg->frequency[0] = center_freq; // cfg->center_frequency = center_freq; // actually applied in the sdr event - sdr_set_center_freq(cfg->dev, center_freq, 0); + sdr_set_center_freq(cfg->dev, center_freq, 1); } -void set_freq_correction(r_cfg_t *cfg, int freq_correction) { +void set_freq_correction(r_cfg_t* cfg, int freq_correction) { // cfg->ppm_error = freq_correction; // actually applied in the sdr event sdr_set_freq_correction(cfg->dev, freq_correction, 0); } -void set_sample_rate(r_cfg_t *cfg, uint32_t sample_rate) { +void set_sample_rate(r_cfg_t* cfg, uint32_t sample_rate) { // cfg->samp_rate = sample_rate; // actually applied in the sdr event sdr_set_sample_rate(cfg->dev, sample_rate, 0); } -void set_gain_str(struct r_cfg *cfg, char const *gain_str) { +void set_gain_str(struct r_cfg* cfg, char const* gain_str) { free(cfg->gain_str); if (!gain_str) { cfg->gain_str = NULL; // auto gain @@ -142,54 +144,53 @@ void set_gain_str(struct r_cfg *cfg, char const *gain_str) { void r_init_cfg(r_cfg_t* cfg) { /* -// cfg->out_block_size = DEFAULT_BUF_LENGTH; -// cfg->samp_rate = DEFAULT_SAMPLE_RATE; -cfg->conversion_mode = CONVERT_NATIVE; -// cfg->fsk_pulse_detect_mode = FSK_PULSE_DETECT_AUTO; -// Default log level is to show all LOG_FATAL, LOG_ERROR, LOG_WARNING -// abnormal messages and LOG_CRITICAL information. -cfg->verbosity = LOG_WARNING; - -// list_ensure_size(&cfg->in_files, 100); -// list_ensure_size(&cfg->output_handler, 16); - -// collect devices list, this should be a module -r_device r_devices[] = { + cfg->out_block_size = DEFAULT_BUF_LENGTH; + cfg->samp_rate = DEFAULT_SAMPLE_RATE; + cfg->conversion_mode = CONVERT_NATIVE; + cfg->fsk_pulse_detect_mode = FSK_PULSE_DETECT_AUTO; + // Default log level is to show all LOG_FATAL, LOG_ERROR, LOG_WARNING + // abnormal messages and LOG_CRITICAL information. + cfg->verbosity = LOG_WARNING; + + list_ensure_size(&cfg->in_files, 100); + list_ensure_size(&cfg->output_handler, 16); + + // collect devices list, this should be a module + r_device r_devices[] = { #define DECL(name) name, - DEVICES + DEVICES #undef DECL -}; + }; -cfg->num_r_devices = sizeof(r_devices) / sizeof(*r_devices); -for (unsigned i = 0; i < cfg->num_r_devices; i++) { - r_devices[i].protocol_num = i + 1; -} -cfg->devices = malloc(sizeof(r_devices)); -if (!cfg->devices) - FATAL_CALLOC("r_init_cfg()"); + cfg->num_r_devices = sizeof(r_devices) / sizeof(*r_devices); + for (unsigned i = 0; i < cfg->num_r_devices; i++) { + r_devices[i].protocol_num = i + 1; + } + cfg->devices = malloc(sizeof(r_devices)); + if (!cfg->devices) + FATAL_CALLOC("r_init_cfg()"); -memcpy(cfg->devices, r_devices, sizeof(r_devices)); + memcpy(cfg->devices, r_devices, sizeof(r_devices)); */ - cfg->demod = calloc(1, sizeof(*cfg->demod)); if (!cfg->demod) FATAL_CALLOC("r_init_cfg()"); - // cfg->demod->level_limit = 0.0; - // cfg->demod->min_level = -12.1442; - // cfg->demod->min_snr = 9.0; + // cfg->demod->level_limit = 0.0; + // cfg->demod->min_level = -12.1442; + // cfg->demod->min_snr = 9.0; // Pulse detect will only print LOG_NOTICE and lower. - // cfg->demod->detect_verbosity = LOG_WARNING; + // cfg->demod->detect_verbosity = LOG_WARNING; // note: this should be optional - // cfg->demod->pulse_detect = pulse_detect_create(); + // cfg->demod->pulse_detect = pulse_detect_create(); // initialize tables - // baseband_init(); + // baseband_init(); - // time(&cfg->frames_since); + // time(&cfg->frames_since); - // list_ensure_size(&cfg->demod->r_devs, 100); - // list_ensure_size(&cfg->demod->dumper, 32); + // list_ensure_size(&cfg->demod->r_devs, 100); + // list_ensure_size(&cfg->demod->dumper, 32); } r_cfg_t* r_create_cfg(void) { @@ -203,7 +204,7 @@ r_cfg_t* r_create_cfg(void) { } /* -void r_free_cfg(r_cfg_t *cfg) { +void r_free_cfg(r_cfg_t* cfg) { if (cfg->dev) { sdr_deactivate(cfg->dev); sdr_close(cfg->dev); @@ -211,8 +212,8 @@ void r_free_cfg(r_cfg_t *cfg) { free(cfg->gain_str); - for (void **iter = cfg->demod->dumper.elems; iter && *iter; ++iter) { - file_info_t const *dumper = *iter; + for (void** iter = cfg->demod->dumper.elems; iter && *iter; ++iter) { + file_info_t const* dumper = *iter; if (dumper->file && (dumper->file != stdout)) fclose(dumper->file); } @@ -225,13 +226,13 @@ void r_free_cfg(r_cfg_t *cfg) { pulse_detect_free(cfg->demod->pulse_detect); -// list_free_elems(&cfg->raw_handler, (list_elem_free_fn)raw_output_free); + list_free_elems(&cfg->raw_handler, (list_elem_free_fn)raw_output_free); r_logger_set_log_handler(NULL, NULL); list_free_elems(&cfg->output_handler, (list_elem_free_fn)data_output_free); -// list_free_elems(&cfg->data_tags, (list_elem_free_fn)data_tag_free); + list_free_elems(&cfg->data_tags, (list_elem_free_fn)data_tag_free); list_free_elems(&cfg->in_files, NULL); @@ -242,7 +243,7 @@ void r_free_cfg(r_cfg_t *cfg) { mg_mgr_free(cfg->mgr); free(cfg->mgr); - // free(cfg); + //free(cfg); } */ @@ -266,8 +267,7 @@ void register_protocol(r_cfg_t* cfg, r_device* r_dev, char* arg) { p = r_dev->create_fn(arg); } else { if (arg && *arg) { - fprintf(stderr, "Protocol [%u] \"%s\" does not take arguments \"%s\"!\n", - r_dev->protocol_num, r_dev->name, arg); + fprintf(stderr, "Protocol [%u] \"%s\" does not take arguments \"%s\"!\n", r_dev->protocol_num, r_dev->name, arg); } p = malloc(sizeof(*p)); if (!p) @@ -276,7 +276,7 @@ void register_protocol(r_cfg_t* cfg, r_device* r_dev, char* arg) { } p->verbose = dev_verbose ? dev_verbose : (cfg->verbosity > 4 ? cfg->verbosity - 5 : 0); - + p->verbose_bits = cfg->verbose_bits; p->log_fn = log_device_handler; p->output_fn = data_acquired_handler; @@ -285,22 +285,20 @@ void register_protocol(r_cfg_t* cfg, r_device* r_dev, char* arg) { list_push(&cfg->demod->r_devs, p); if (cfg->verbosity >= LOG_INFO) { - fprintf(stderr, "Registering protocol [%u] \"%s\"\n", r_dev->protocol_num, - r_dev->name); + fprintf(stderr, "Registering protocol [%u] \"%s\"\n", r_dev->protocol_num, r_dev->name); } } /* -void free_protocol(r_device *r_dev) { +void free_protocol(r_device* r_dev) { // free(r_dev->name); free(r_dev->decode_ctx); free(r_dev); } -void unregister_protocol(r_cfg_t *cfg, r_device *r_dev) { - for (size_t i = 0; i < cfg->demod->r_devs.len; - ++i) { // list might contain NULLs - r_device *p = cfg->demod->r_devs.elems[i]; +void unregister_protocol(r_cfg_t* cfg, r_device* r_dev) { + for (size_t i = 0; i < cfg->demod->r_devs.len; ++i) { // list might contain NULLs + r_device* p = cfg->demod->r_devs.elems[i]; if (!strcmp(p->name, r_dev->name)) { list_remove(&cfg->demod->r_devs, i, (list_elem_free_fn)free_protocol); i--; // so we don't skip the next elem now shifted down @@ -308,7 +306,7 @@ void unregister_protocol(r_cfg_t *cfg, r_device *r_dev) { } } -void register_all_protocols(r_cfg_t *cfg, unsigned disabled) { +void register_all_protocols(r_cfg_t* cfg, unsigned disabled) { for (int i = 0; i < cfg->num_r_devices; i++) { // register all device protocols that are not disabled if (cfg->devices[i].disabled <= disabled) { @@ -317,53 +315,39 @@ void register_all_protocols(r_cfg_t *cfg, unsigned disabled) { } } */ - /* output helper */ - /* -void calc_rssi_snr(r_cfg_t *cfg, pulse_data_t *pulse_data) { - float ook_high_estimate = - pulse_data->ook_high_estimate > 0 ? pulse_data->ook_high_estimate : 1; - float ook_low_estimate = - pulse_data->ook_low_estimate > 0 ? pulse_data->ook_low_estimate : 1; +void calc_rssi_snr(r_cfg_t* cfg, pulse_data_t* pulse_data) { + float ook_high_estimate = pulse_data->ook_high_estimate > 0 ? pulse_data->ook_high_estimate : 1; + float ook_low_estimate = pulse_data->ook_low_estimate > 0 ? pulse_data->ook_low_estimate : 1; float asnr = ook_high_estimate / ook_low_estimate; - //float foffs1 = - // (float)pulse_data->fsk_f1_est / INT16_MAX * cfg->samp_rate / 2.0; - // float foffs2 = - // (float)pulse_data->fsk_f2_est / INT16_MAX * cfg->samp_rate / 2.0; - //pulse_data->freq1_hz = (foffs1 + cfg->center_frequency); - //pulse_data->freq2_hz = (foffs2 + cfg->center_frequency); - //pulse_data->centerfreq_hz = cfg->center_frequency; + float foffs1 = (float)pulse_data->fsk_f1_est / INT16_MAX * cfg->samp_rate / 2.0; + float foffs2 = (float)pulse_data->fsk_f2_est / INT16_MAX * cfg->samp_rate / 2.0; + pulse_data->freq1_hz = (foffs1 + cfg->center_frequency); + pulse_data->freq2_hz = (foffs2 + cfg->center_frequency); + pulse_data->centerfreq_hz = cfg->center_frequency; pulse_data->depth_bits = cfg->demod->sample_size * 4; // NOTE: for (CU8) amplitude is 10x (because it's squares) - if (cfg->demod->sample_size == 2 && - !cfg->demod->use_mag_est) { // amplitude (CU8) + if (cfg->demod->sample_size == 2 && !cfg->demod->use_mag_est) { // amplitude (CU8) pulse_data->range_db = 42.1442f; // 10*log10f(16384.0f) == 20*log10f(128.0f) - pulse_data->rssi_db = - 10.0f * log10f(ook_high_estimate) - 42.1442f; // 10*log10f(16384.0f) - pulse_data->noise_db = - 10.0f * log10f(ook_low_estimate) - 42.1442f; // 10*log10f(16384.0f) + pulse_data->rssi_db = 10.0f * log10f(ook_high_estimate) - 42.1442f; // 10*log10f(16384.0f) + pulse_data->noise_db = 10.0f * log10f(ook_low_estimate) - 42.1442f; // 10*log10f(16384.0f) pulse_data->snr_db = 10.0f * log10f(asnr); - } else { // magnitude (CU8, CS16) + } else { // magnitude (CU8, CS16) pulse_data->range_db = 84.2884f; // 20*log10f(16384.0f) - // lowest (scaled x128) reading at 8 bit is -20*log10(128) = -42.1442 (eff. - // -36 dB) lowest (scaled div2) reading at 12 bit is -20*log10(1024) = - // -60.2060 (eff. -54 dB) lowest (scaled div2) reading at 16 bit is - // -20*log10(16384) = -84.2884 (eff. -78 dB) - pulse_data->rssi_db = - 20.0f * log10f(ook_high_estimate) - 84.2884f; // 20*log10f(16384.0f) - pulse_data->noise_db = - 20.0f * log10f(ook_low_estimate) - 84.2884f; // 20*log10f(16384.0f) + // lowest (scaled x128) reading at 8 bit is -20*log10(128) = -42.1442 (eff. -36 dB) + // lowest (scaled div2) reading at 12 bit is -20*log10(1024) = -60.2060 (eff. -54 dB) + // lowest (scaled div2) reading at 16 bit is -20*log10(16384) = -84.2884 (eff. -78 dB) + pulse_data->rssi_db = 20.0f * log10f(ook_high_estimate) - 84.2884f; // 20*log10f(16384.0f) + pulse_data->noise_db = 20.0f * log10f(ook_low_estimate) - 84.2884f; // 20*log10f(16384.0f) pulse_data->snr_db = 20.0f * log10f(asnr); } } -*/ -/* -char *time_pos_str(r_cfg_t *cfg, unsigned samples_ago, char *buf) { + +char* time_pos_str(r_cfg_t* cfg, unsigned samples_ago, char* buf) { if (cfg->report_time == REPORT_TIME_SAMPLES) { double s_per_sample = 1.0 / cfg->samp_rate; - return sample_pos_str( - cfg->demod->sample_file_pos - samples_ago * s_per_sample, buf); + return sample_pos_str(cfg->demod->sample_file_pos - samples_ago * s_per_sample, buf); } else { struct timeval ago = cfg->demod->now; double us_per_sample = 1e6 / cfg->samp_rate; @@ -374,7 +358,7 @@ char *time_pos_str(r_cfg_t *cfg, unsigned samples_ago, char *buf) { } ago.tv_usec -= usecs_ago; - char const *format = NULL; + char const* format = NULL; if (cfg->report_time == REPORT_TIME_UNIX) format = "%s"; else if (cfg->report_time == REPORT_TIME_ISO) @@ -386,17 +370,14 @@ char *time_pos_str(r_cfg_t *cfg, unsigned samples_ago, char *buf) { return format_time_str(buf, format, cfg->report_time_tz, ago.tv_sec); } } -*/ -// well-known fields "time", "msg" and "codes" are used to output general -// decoder messages well-known field "bits" is only used when verbose bits (-M -// bits) is requested well-known field "tag" is only used when output tagging is -// requested well-known field "protocol" is only used when model protocol is -// requested well-known field "description" is only used when model description -// is requested well-known fields "mod", "freq", "freq1", "freq2", "rssi", -// "snr", "noise" are used by meta report option -/* -char const **well_known_output_fields(r_cfg_t *cfg) { +// well-known fields "time", "msg" and "codes" are used to output general decoder messages +// well-known field "bits" is only used when verbose bits (-M bits) is requested +// well-known field "tag" is only used when output tagging is requested +// well-known field "protocol" is only used when model protocol is requested +// well-known field "description" is only used when model description is requested +// well-known fields "mod", "freq", "freq1", "freq2", "rssi", "snr", "noise" are used by meta report option +char const** well_known_output_fields(r_cfg_t* cfg) { list_t field_list = {0}; list_ensure_size(&field_list, 15); @@ -407,12 +388,12 @@ char const **well_known_output_fields(r_cfg_t *cfg) { if (cfg->verbose_bits) list_push(&field_list, "bits"); - for (void **iter = cfg->data_tags.elems; iter && *iter; ++iter) { - data_tag_t *tag = *iter; + for (void** iter = cfg->data_tags.elems; iter && *iter; ++iter) { + data_tag_t* tag = *iter; if (tag->key) { - list_push(&field_list, (void *)tag->key); + list_push(&field_list, (void*)tag->key); } else { - list_push_all(&field_list, (void **)tag->includes); + list_push_all(&field_list, (void**)tag->includes); } } @@ -430,16 +411,14 @@ char const **well_known_output_fields(r_cfg_t *cfg) { list_push(&field_list, "noise"); } - return (char const **)field_list.elems; + return (char const**)field_list.elems; } */ - -/** Convert CSV keys according to selected conversion mode. Replacement is - * static but in-place. */ +/** Convert CSV keys according to selected conversion mode. Replacement is static but in-place. */ /* -static char const **convert_csv_fields(r_cfg_t *cfg, char const **fields) { +static char const** convert_csv_fields(r_cfg_t* cfg, char const** fields) { if (cfg->conversion_mode == CONVERT_SI) { - for (char const **p = fields; *p; ++p) { + for (char const** p = fields; *p; ++p) { if (!strcmp(*p, "temperature_F")) *p = "temperature_C"; else if (!strcmp(*p, "pressure_PSI")) @@ -456,7 +435,7 @@ static char const **convert_csv_fields(r_cfg_t *cfg, char const **fields) { } if (cfg->conversion_mode == CONVERT_CUSTOMARY) { - for (char const **p = fields; *p; ++p) { + for (char const** p = fields; *p; ++p) { if (!strcmp(*p, "temperature_C")) *p = "temperature_F"; else if (!strcmp(*p, "temperature_1_C")) @@ -483,31 +462,28 @@ static char const **convert_csv_fields(r_cfg_t *cfg, char const **fields) { } // find the fields output for CSV -char const **determine_csv_fields(r_cfg_t *cfg, char const *const *well_known, - int *num_fields) { +char const** determine_csv_fields(r_cfg_t* cfg, char const* const* well_known, int* num_fields) { list_t field_list = {0}; list_ensure_size(&field_list, 100); // always add well-known fields - list_push_all(&field_list, (void **)well_known); + list_push_all(&field_list, (void**)well_known); - list_t *r_devs = &cfg->demod->r_devs; - for (void **iter = r_devs->elems; iter && *iter; ++iter) { - r_device *r_dev = *iter; + list_t* r_devs = &cfg->demod->r_devs; + for (void** iter = r_devs->elems; iter && *iter; ++iter) { + r_device* r_dev = *iter; if (r_dev->fields) - list_push_all(&field_list, (void **)r_dev->fields); + list_push_all(&field_list, (void**)r_dev->fields); else - fprintf(stderr, - "rtl_433: warning: %u \"%s\" does not support CSV output\n", + fprintf(stderr, "rtl_433: warning: %u \"%s\" does not support CSV output\n", r_dev->protocol_num, r_dev->name); } - convert_csv_fields(cfg, (char const **)field_list.elems); + convert_csv_fields(cfg, (char const**)field_list.elems); if (num_fields) *num_fields = field_list.len; - return (char const **)field_list.elems; + return (char const**)field_list.elems; } - */ int run_ook_demods(list_t* r_devs, pulse_data_t* pulse_data) { @@ -515,8 +491,7 @@ int run_ook_demods(list_t* r_devs, pulse_data_t* pulse_data) { unsigned next_priority = 0; // next smallest on each loop through decoders // run all decoders of each priority, stop if an event is produced - for (unsigned priority = 0; !p_events && priority < UINT_MAX; - priority = next_priority) { + for (unsigned priority = 0; !p_events && priority < UINT_MAX; priority = next_priority) { next_priority = UINT_MAX; for (void** iter = r_devs->elems; iter && *iter; ++iter) { r_device* r_dev = *iter; @@ -527,13 +502,14 @@ int run_ook_demods(list_t* r_devs, pulse_data_t* pulse_data) { // Run only current priority if (r_dev->priority != priority) continue; + +//TODO: rtl_433_ESP #ifdef RTL_DEBUG // logprintfLn(LOG_DEBUG, "demod(%d) - %s", r_dev->modulation, r_dev->name); #endif #ifdef RESOURCE_DEBUG int preStack = uxTaskGetStackHighWaterMark(NULL); #endif - switch (r_dev->modulation) { case OOK_PULSE_PCM: // case OOK_PULSE_RZ: @@ -569,9 +545,9 @@ int run_ook_demods(list_t* r_devs, pulse_data_t* pulse_data) { case FSK_PULSE_MANCHESTER_ZEROBIT: break; default: - fprintf(stderr, "Unknown modulation %u in protocol!\n", - r_dev->modulation); + fprintf(stderr, "Unknown modulation %u in protocol!\n", r_dev->modulation); } +//TODO: rtl_433_ESP #ifdef RESOURCE_DEBUG int delta = preStack - uxTaskGetStackHighWaterMark(NULL); if (delta) { @@ -582,7 +558,8 @@ int run_ook_demods(list_t* r_devs, pulse_data_t* pulse_data) { #ifdef RTL_ANALYZE // logprintfLn(LOG_DEBUG, "RTL_ANALYZE_MODEL %s==%d", r_dev->name, r_dev->protocol_num); if (r_dev->protocol_num == RTL_ANALYZE) { - pulse_analyzer(pulse_data, 1); + r_device device = {.log_fn = log_device_handler, .output_ctx = r_dev->output_ctx}; + pulse_analyzer(pulse_data, PULSE_DATA_OOK, &device); } #endif } @@ -596,8 +573,7 @@ int run_fsk_demods(list_t* r_devs, pulse_data_t* fsk_pulse_data) { unsigned next_priority = 0; // next smallest on each loop through decoders // run all decoders of each priority, stop if an event is produced - for (unsigned priority = 0; !p_events && priority < UINT_MAX; - priority = next_priority) { + for (unsigned priority = 0; !p_events && priority < UINT_MAX; priority = next_priority) { next_priority = UINT_MAX; for (void** iter = r_devs->elems; iter && *iter; ++iter) { r_device* r_dev = *iter; @@ -609,12 +585,14 @@ int run_fsk_demods(list_t* r_devs, pulse_data_t* fsk_pulse_data) { if (r_dev->priority != priority) continue; +//TODO: rtl_433_ESP #ifdef RTL_DEBUG // logprintfLn(LOG_DEBUG, "demod(%d) - %s", r_dev->modulation, r_dev->name); #endif #ifdef RESOURCE_DEBUG int preStack = uxTaskGetStackHighWaterMark(NULL); #endif + switch (r_dev->modulation) { // OOK decoders case OOK_PULSE_PCM: @@ -638,15 +616,23 @@ int run_fsk_demods(list_t* r_devs, pulse_data_t* fsk_pulse_data) { p_events += pulse_slicer_manchester_zerobit(fsk_pulse_data, r_dev); break; default: - fprintf(stderr, "Unknown modulation %u in protocol!\n", - r_dev->modulation); + fprintf(stderr, "Unknown modulation %u in protocol!\n", r_dev->modulation); } + +//TODO: rtl_433_ESP #ifdef RESOURCE_DEBUG int delta = preStack - uxTaskGetStackHighWaterMark(NULL); if (delta) { logprintfLn(LOG_DEBUG, "Process rtl_433_DecoderTask resource hit demod(%d) - %s, delta %d, stack free: %u", r_dev->modulation, r_dev->name, delta, uxTaskGetStackHighWaterMark(NULL)); } +#endif +#ifdef RTL_ANALYZE + // logprintfLn(LOG_DEBUG, "RTL_ANALYZE_MODEL %s==%d", r_dev->name, r_dev->protocol_num); + if (r_dev->protocol_num == RTL_ANALYZE) { + r_device device = {.log_fn = log_device_handler, .output_ctx = r_dev->output_ctx}; + pulse_analyzer(fsk_pulse_data, PULSE_DATA_FSK, &device); + } #endif } } @@ -657,34 +643,32 @@ int run_fsk_demods(list_t* r_devs, pulse_data_t* fsk_pulse_data) { /* handlers */ /* - -static void log_handler(log_level_t level, char const *src, char const *msg, - void *userdata) { - r_cfg_t *cfg = userdata; +static void log_handler(log_level_t level, char const* src, char const* msg, void* userdata) { + r_cfg_t* cfg = userdata; if (cfg->verbosity < (int)level) { return; } - // clang-format off + /* clang-format off */ +/* data_t *data = data_make( "src", "", DATA_STRING, src, "lvl", "", DATA_INT, level, "msg", "", DATA_STRING, msg, NULL); - // clang-format on - + /* clang-format on */ +/* // prepend "time" if requested - if (cfg->report_time != REPORT_TIME_OFF) { char time_str[LOCAL_TIME_BUFLEN]; time_pos_str(cfg, 0, time_str); - data = data_prepend(data, "time", "", DATA_STRING, time_str, NULL); + data = data_prepend(data, + "time", "", DATA_STRING, time_str, + NULL); } - - for (size_t i = 0; i < cfg->output_handler.len; - ++i) { // list might contain NULLs - data_output_t *output = cfg->output_handler.elems[i]; + for (size_t i = 0; i < cfg->output_handler.len; ++i) { // list might contain NULLs + data_output_t* output = cfg->output_handler.elems[i]; if (output && output->log_level >= (int)level) { data_output_print(output, data); } @@ -692,24 +676,24 @@ static void log_handler(log_level_t level, char const *src, char const *msg, data_free(data); } - -void r_redirect_logging(r_cfg_t *cfg) { +void r_redirect_logging(r_cfg_t* cfg) { r_logger_set_log_handler(log_handler, cfg); } -*/ + /** Pass the data structure to all output handlers. Frees data afterwards. */ /* -void event_occurred_handler(r_cfg_t *cfg, data_t *data) { +void event_occurred_handler(r_cfg_t* cfg, data_t* data) { // prepend "time" if requested if (cfg->report_time != REPORT_TIME_OFF) { char time_str[LOCAL_TIME_BUFLEN]; time_pos_str(cfg, 0, time_str); - data = data_prepend(data, "time", "", DATA_STRING, time_str, NULL); + data = data_prepend(data, + "time", "", DATA_STRING, time_str, + NULL); } - for (size_t i = 0; i < cfg->output_handler.len; - ++i) { // list might contain NULLs - data_output_t *output = cfg->output_handler.elems[i]; + for (size_t i = 0; i < cfg->output_handler.len; ++i) { // list might contain NULLs + data_output_t* output = cfg->output_handler.elems[i]; data_output_print(output, data); } data_free(data); @@ -717,20 +701,21 @@ void event_occurred_handler(r_cfg_t *cfg, data_t *data) { */ /** Pass the data structure to all output handlers. Frees data afterwards. */ - void log_device_handler(r_device* r_dev, int level, data_t* data) { r_cfg_t* cfg = r_dev->output_ctx; + // prepend "time" if requested /* - if (cfg->report_time != REPORT_TIME_OFF) { - char time_str[LOCAL_TIME_BUFLEN]; - time_pos_str(cfg, cfg->demod->pulse_data.start_ago, time_str); - data = data_prepend(data, "time", "", DATA_STRING, time_str, NULL); - } - */ - - for (size_t i = 0; i < cfg->output_handler.len; - ++i) { // list might contain NULLs + if (cfg->report_time != REPORT_TIME_OFF) { + char time_str[LOCAL_TIME_BUFLEN]; + time_pos_str(cfg, cfg->demod->pulse_data.start_ago, time_str); + data = data_prepend(data, + "time", "", DATA_STRING, time_str, + NULL); + } +*/ + + for (size_t i = 0; i < cfg->output_handler.len; ++i) { // list might contain NULLs data_output_t* output = cfg->output_handler.elems[i]; if (output && output->log_level >= level) { data_output_print(output, data); @@ -740,7 +725,6 @@ void log_device_handler(r_device* r_dev, int level, data_t* data) { } /** Pass the data structure to all output handlers. Frees data afterwards. */ - void data_acquired_handler(r_device* r_dev, data_t* data) { r_cfg_t* cfg = r_dev->output_ctx; @@ -755,8 +739,7 @@ void data_acquired_handler(r_device* r_dev, data_t* data) { } } if (!found) { - fprintf(stderr, "WARNING: Undeclared field \"%s\" in [%u] \"%s\"\n", - d->key, r_dev->protocol_num, r_dev->name); + fprintf(stderr, "WARNING: Undeclared field \"%s\" in [%u] \"%s\"\n", d->key, r_dev->protocol_num, r_dev->name); } } #endif @@ -797,7 +780,6 @@ void data_acquired_handler(r_device* r_dev, data_t* data) { // Convert double type fields ending in _in to _mm else if ((d->type == DATA_DOUBLE) && (str_endswith(d->key, "_in") || str_endswith(d->key, "_inch"))) { - d->value.v_dbl = inch2mm(d->value.v_dbl); // need to free ptr returned from str_replace char* new_label1 = str_replace(d->key, "_inch", "_in"); char* new_label2 = str_replace(new_label1, "_in", "_mm"); @@ -917,66 +899,67 @@ void data_acquired_handler(r_device* r_dev, data_t* data) { } /* - // prepend "description" if requested - if (cfg->report_description) { - data = data_prepend(data, "description", "Description", DATA_STRING, - r_dev->name, NULL); - } - - // prepend "protocol" if requested - if (cfg->report_protocol && r_dev->protocol_num) { - data = data_prepend(data, "protocol", "Protocol", DATA_INT, - r_dev->protocol_num, NULL); - } - + // prepend "description" if requested + if (cfg->report_description) { + data = data_prepend(data, + "description", "Description", DATA_STRING, r_dev->name, + NULL); + } - if (cfg->report_meta && cfg->demod->fsk_pulse_data.fsk_f2_est) { - data_append(data, "mod", "Modulation", DATA_STRING, "FSK", "freq1", - "Freq1", DATA_FORMAT, "%.1f MHz", DATA_DOUBLE, - cfg->demod->fsk_pulse_data.freq1_hz / 1000000.0, "freq2", - "Freq2", DATA_FORMAT, "%.1f MHz", DATA_DOUBLE, - cfg->demod->fsk_pulse_data.freq2_hz / 1000000.0, "rssi", - "RSSI", DATA_FORMAT, "%.1f dB", DATA_DOUBLE, - cfg->demod->fsk_pulse_data.rssi_db, "snr", "SNR", DATA_FORMAT, - "%.1f dB", DATA_DOUBLE, cfg->demod->fsk_pulse_data.snr_db, - "noise", "Noise", DATA_FORMAT, "%.1f dB", DATA_DOUBLE, - cfg->demod->fsk_pulse_data.noise_db, NULL); - } else if (cfg->report_meta) { - data_append(data, "mod", "Modulation", DATA_STRING, "ASK", "freq", "Freq", - DATA_FORMAT, "%.1f MHz", DATA_DOUBLE, - cfg->demod->pulse_data.freq1_hz / 1000000.0, "rssi", "RSSI", - DATA_FORMAT, "%.1f dB", DATA_DOUBLE, - cfg->demod->pulse_data.rssi_db, "snr", "SNR", DATA_FORMAT, - "%.1f dB", DATA_DOUBLE, cfg->demod->pulse_data.snr_db, - "noise", "Noise", DATA_FORMAT, "%.1f dB", DATA_DOUBLE, - cfg->demod->pulse_data.noise_db, NULL); - } + // prepend "protocol" if requested + if (cfg->report_protocol && r_dev->protocol_num) { + data = data_prepend(data, + "protocol", "Protocol", DATA_INT, r_dev->protocol_num, + NULL); + } - // prepend "time" if requested - if (cfg->report_time != REPORT_TIME_OFF) { - char time_str[LOCAL_TIME_BUFLEN]; - time_pos_str(cfg, cfg->demod->pulse_data.start_ago, time_str); - data = data_prepend(data, "time", "", DATA_STRING, time_str, NULL); - } + if (cfg->report_meta && cfg->demod->fsk_pulse_data.fsk_f2_est) { + data_append(data, + "mod", "Modulation", DATA_STRING, "FSK", + "freq1", "Freq1", DATA_FORMAT, "%.1f MHz", DATA_DOUBLE, cfg->demod->fsk_pulse_data.freq1_hz / 1000000.0, + "freq2", "Freq2", DATA_FORMAT, "%.1f MHz", DATA_DOUBLE, cfg->demod->fsk_pulse_data.freq2_hz / 1000000.0, + "rssi", "RSSI", DATA_FORMAT, "%.1f dB", DATA_DOUBLE, cfg->demod->fsk_pulse_data.rssi_db, + "snr", "SNR", DATA_FORMAT, "%.1f dB", DATA_DOUBLE, cfg->demod->fsk_pulse_data.snr_db, + "noise", "Noise", DATA_FORMAT, "%.1f dB", DATA_DOUBLE, cfg->demod->fsk_pulse_data.noise_db, + NULL); + } else if (cfg->report_meta) { + data_append(data, + "mod", "Modulation", DATA_STRING, "ASK", + "freq", "Freq", DATA_FORMAT, "%.1f MHz", DATA_DOUBLE, cfg->demod->pulse_data.freq1_hz / 1000000.0, + "rssi", "RSSI", DATA_FORMAT, "%.1f dB", DATA_DOUBLE, cfg->demod->pulse_data.rssi_db, + "snr", "SNR", DATA_FORMAT, "%.1f dB", DATA_DOUBLE, cfg->demod->pulse_data.snr_db, + "noise", "Noise", DATA_FORMAT, "%.1f dB", DATA_DOUBLE, cfg->demod->pulse_data.noise_db, + NULL); + } - // apply all tags - for (void **iter = cfg->data_tags.elems; iter && *iter; ++iter) { - data_tag_t *tag = *iter; - data = data_tag_apply(tag, data, cfg->in_filename); - } + // prepend "time" if requested + if (cfg->report_time != REPORT_TIME_OFF) { + char time_str[LOCAL_TIME_BUFLEN]; + time_pos_str(cfg, cfg->demod->pulse_data.start_ago, time_str); + data = data_prepend(data, + "time", "", DATA_STRING, time_str, + NULL); + } + // apply all tags + for (void** iter = cfg->data_tags.elems; iter && *iter; ++iter) { + data_tag_t* tag = *iter; + data = data_tag_apply(tag, data, cfg->in_filename); + } */ - for (size_t i = 0; i < cfg->output_handler.len; - ++i) { // list might contain NULLs + for (size_t i = 0; i < cfg->output_handler.len; ++i) { // list might contain NULLs data_output_t* output = cfg->output_handler.elems[i]; data_output_print(output, data); } + //TODO: rtl_433_ESP data_append(data, "protocol", "", DATA_STRING, r_dev->name, "rssi", "RSSI", DATA_INT, cfg->demod->pulse_data.signalRssi, "duration", "", DATA_INT, cfg->demod->pulse_data.signalDuration, NULL); data_print_jsons(data, cfg->messageBuffer, cfg->bufferSize); + +//TODO: rtl_433_ESP #ifdef DEMOD_DEBUG logprintfLn(LOG_INFO, "data_output %s", cfg->messageBuffer); #endif @@ -987,17 +970,16 @@ void data_acquired_handler(r_device* r_dev, data_t* data) { data_free(data); } -// level 0: do not report (don't call this), 1: report successful devices, 2: -// report active devices, 3: report all +// level 0: do not report (don't call this), 1: report successful devices, 2: report active devices, 3: report all /* -data_t *create_report_data(r_cfg_t *cfg, int level) { - list_t *r_devs = &cfg->demod->r_devs; - data_t *data; +data_t* create_report_data(r_cfg_t* cfg, int level) { + list_t* r_devs = &cfg->demod->r_devs; + data_t* data; list_t dev_data_list = {0}; list_ensure_size(&dev_data_list, r_devs->len); - for (void **iter = r_devs->elems; iter && *iter; ++iter) { - r_device *r_dev = *iter; + for (void** iter = r_devs->elems; iter && *iter; ++iter) { + r_device* r_dev = *iter; if (level <= 2 && r_dev->decode_events == 0) continue; if (level <= 1 && r_dev->decode_ok == 0) @@ -1005,57 +987,68 @@ data_t *create_report_data(r_cfg_t *cfg, int level) { if (level <= 0) continue; - data = data_make("device", "", DATA_INT, r_dev->protocol_num, "name", "", - DATA_STRING, r_dev->name, "events", "", DATA_INT, - r_dev->decode_events, "ok", "", DATA_INT, r_dev->decode_ok, - "messages", "", DATA_INT, r_dev->decode_messages, NULL); + data = data_make( + "device", "", DATA_INT, r_dev->protocol_num, + "name", "", DATA_STRING, r_dev->name, + "events", "", DATA_INT, r_dev->decode_events, + "ok", "", DATA_INT, r_dev->decode_ok, + "messages", "", DATA_INT, r_dev->decode_messages, + NULL); if (r_dev->decode_fails[-DECODE_FAIL_OTHER]) - data_append(data, "fail_other", "", DATA_INT, - r_dev->decode_fails[-DECODE_FAIL_OTHER], NULL); + data_append(data, + "fail_other", "", DATA_INT, r_dev->decode_fails[-DECODE_FAIL_OTHER], + NULL); if (r_dev->decode_fails[-DECODE_ABORT_LENGTH]) - data_append(data, "abort_length", "", DATA_INT, - r_dev->decode_fails[-DECODE_ABORT_LENGTH], NULL); + data_append(data, + "abort_length", "", DATA_INT, r_dev->decode_fails[-DECODE_ABORT_LENGTH], + NULL); if (r_dev->decode_fails[-DECODE_ABORT_EARLY]) - data_append(data, "abort_early", "", DATA_INT, - r_dev->decode_fails[-DECODE_ABORT_EARLY], NULL); + data_append(data, + "abort_early", "", DATA_INT, r_dev->decode_fails[-DECODE_ABORT_EARLY], + NULL); if (r_dev->decode_fails[-DECODE_FAIL_MIC]) - data_append(data, "fail_mic", "", DATA_INT, - r_dev->decode_fails[-DECODE_FAIL_MIC], NULL); + data_append(data, + "fail_mic", "", DATA_INT, r_dev->decode_fails[-DECODE_FAIL_MIC], + NULL); if (r_dev->decode_fails[-DECODE_FAIL_SANITY]) - data_append(data, "fail_sanity", "", DATA_INT, - r_dev->decode_fails[-DECODE_FAIL_SANITY], NULL); + data_append(data, + "fail_sanity", "", DATA_INT, r_dev->decode_fails[-DECODE_FAIL_SANITY], + NULL); list_push(&dev_data_list, data); } - data = data_make("count", "", DATA_INT, cfg->frames_count, "fsk", "", - DATA_INT, cfg->frames_fsk, "events", "", DATA_INT, - cfg->frames_events, NULL); + data = data_make( + "count", "", DATA_INT, cfg->frames_count, + "fsk", "", DATA_INT, cfg->frames_fsk, + "events", "", DATA_INT, cfg->frames_events, + NULL); char since_str[LOCAL_TIME_BUFLEN]; - format_time_str(since_str, "%Y-%m-%dT%H:%M:%S", cfg->report_time_tz, - cfg->frames_since); + format_time_str(since_str, "%Y-%m-%dT%H:%M:%S", cfg->report_time_tz, cfg->frames_since); data = data_make( - "enabled", "", DATA_INT, r_devs->len, "since", "", DATA_STRING, since_str, - "frames", "", DATA_DATA, data, "stats", "", DATA_ARRAY, - data_array(dev_data_list.len, DATA_DATA, dev_data_list.elems), NULL); + "enabled", "", DATA_INT, r_devs->len, + "since", "", DATA_STRING, since_str, + "frames", "", DATA_DATA, data, + "stats", "", DATA_ARRAY, data_array(dev_data_list.len, DATA_DATA, dev_data_list.elems), + NULL); list_free_elems(&dev_data_list, NULL); return data; } -void flush_report_data(r_cfg_t *cfg) { - list_t *r_devs = &cfg->demod->r_devs; +void flush_report_data(r_cfg_t* cfg) { + list_t* r_devs = &cfg->demod->r_devs; time(&cfg->frames_since); cfg->frames_count = 0; cfg->frames_fsk = 0; cfg->frames_events = 0; - for (void **iter = r_devs->elems; iter && *iter; ++iter) { - r_device *r_dev = *iter; + for (void** iter = r_devs->elems; iter && *iter; ++iter) { + r_device* r_dev = *iter; r_dev->decode_events = 0; r_dev->decode_ok = 0; @@ -1106,130 +1099,120 @@ static int lvlarg_param(char** param, int default_verb) { return val; } -static FILE* fopen_output(char* param) { - FILE* file; - if (!param || !*param || (*param == '-' && param[1] == '\0')) { - return stdout; +/// Opens the path @p param (or STDOUT if empty or `-`) for append writing, removes leading `,` and `:` from path name. +static FILE* fopen_output(char const* param) { + if (!param || !*param) { + return stdout; // No path given + } + while (*param == ',') { + param++; // Skip all leading `,` + } + if (*param == ':') { + param++; // Skip one leading `:` } - file = fopen(param, "a"); + if (*param == '-' && param[1] == '\0') { + return stdout; // STDOUT requested + } + FILE* file = fopen(param, "a"); if (!file) { fprintf(stderr, "rtl_433: failed to open output file\n"); exit(1); } return file; } -/* -void add_json_output(r_cfg_t *cfg, char *param) { +/* +void add_json_output(r_cfg_t* cfg, char* param) { int log_level = lvlarg_param(¶m, 0); - list_push(&cfg->output_handler, - data_output_json_create(log_level, fopen_output(param))); + list_push(&cfg->output_handler, data_output_json_create(log_level, fopen_output(param))); } -void add_csv_output(r_cfg_t *cfg, char *param) { +void add_csv_output(r_cfg_t* cfg, char* param) { int log_level = lvlarg_param(¶m, 0); - list_push(&cfg->output_handler, - data_output_csv_create(log_level, fopen_output(param))); + list_push(&cfg->output_handler, data_output_csv_create(log_level, fopen_output(param))); } -*/ -/* -void start_outputs(r_cfg_t *cfg, char const *const *well_known) { +void start_outputs(r_cfg_t* cfg, char const* const* well_known) { int num_output_fields; - char const **output_fields = - determine_csv_fields(cfg, well_known, &num_output_fields); + char const** output_fields = determine_csv_fields(cfg, well_known, &num_output_fields); - for (size_t i = 0; i < cfg->output_handler.len; - ++i) { // list might contain NULLs - data_output_t *output = cfg->output_handler.elems[i]; + for (size_t i = 0; i < cfg->output_handler.len; ++i) { // list might contain NULLs + data_output_t* output = cfg->output_handler.elems[i]; data_output_start(output, output_fields, num_output_fields); } - free((void *)output_fields); + free((void*)output_fields); } */ void add_log_output(r_cfg_t* cfg, char* param) { int log_level = lvlarg_param(¶m, LOG_TRACE); - list_push(&cfg->output_handler, - data_output_log_create(log_level, fopen_output(param))); + list_push(&cfg->output_handler, data_output_log_create(log_level, fopen_output(param))); } /* -void add_kv_output(r_cfg_t *cfg, char *param) { +void add_kv_output(r_cfg_t* cfg, char* param) { int log_level = lvlarg_param(¶m, LOG_TRACE); - list_push(&cfg->output_handler, - data_output_kv_create(log_level, fopen_output(param))); + list_push(&cfg->output_handler, data_output_kv_create(log_level, fopen_output(param))); } -void add_mqtt_output(r_cfg_t *cfg, char *param) { - list_push(&cfg->output_handler, - data_output_mqtt_create(get_mgr(cfg), param, cfg->dev_query)); +void add_mqtt_output(r_cfg_t* cfg, char* param) { + list_push(&cfg->output_handler, data_output_mqtt_create(get_mgr(cfg), param, cfg->dev_query)); } -void add_influx_output(r_cfg_t *cfg, char *param) { - list_push(&cfg->output_handler, - data_output_influx_create(get_mgr(cfg), param)); +void add_influx_output(r_cfg_t* cfg, char* param) { + list_push(&cfg->output_handler, data_output_influx_create(get_mgr(cfg), param)); } -void add_syslog_output(r_cfg_t *cfg, char *param) { +void add_syslog_output(r_cfg_t* cfg, char* param) { int log_level = lvlarg_param(¶m, LOG_WARNING); - char const *host = "localhost"; - char const *port = "514"; - char const *extra = hostport_param(param, &host, &port); + char const* host = "localhost"; + char const* port = "514"; + char const* extra = hostport_param(param, &host, &port); if (extra && *extra) { print_logf(LOG_FATAL, "Syslog UDP", "Unknown parameters \"%s\"", extra); } - print_logf(LOG_CRITICAL, "Syslog UDP", "Sending datagrams to %s port %s", - host, port); + print_logf(LOG_CRITICAL, "Syslog UDP", "Sending datagrams to %s port %s", host, port); - list_push(&cfg->output_handler, - data_output_syslog_create(log_level, host, port)); + list_push(&cfg->output_handler, data_output_syslog_create(log_level, host, port)); } -void add_http_output(r_cfg_t *cfg, char *param) { +void add_http_output(r_cfg_t* cfg, char* param) { // Note: no log_level, the HTTP-API consumes all log levels. - char const *host = "0.0.0.0"; - char const *port = "8433"; - char const *extra = hostport_param(param, &host, &port); + char const* host = "0.0.0.0"; + char const* port = "8433"; + char const* extra = hostport_param(param, &host, &port); if (extra && *extra) { print_logf(LOG_FATAL, "HTTP server", "Unknown parameters \"%s\"", extra); } - print_logf(LOG_CRITICAL, "HTTP server", "Starting HTTP server at %s port %s", - host, port); + print_logf(LOG_CRITICAL, "HTTP server", "Starting HTTP server at %s port %s", host, port); - list_push(&cfg->output_handler, - data_output_http_create(get_mgr(cfg), host, port, cfg)); + list_push(&cfg->output_handler, data_output_http_create(get_mgr(cfg), host, port, cfg)); } -void add_trigger_output(r_cfg_t *cfg, char *param) { +void add_trigger_output(r_cfg_t* cfg, char* param) { // Note: no log_level, we never trigger on logs. - list_push(&cfg->output_handler, - data_output_trigger_create(fopen_output(param))); + list_push(&cfg->output_handler, data_output_trigger_create(fopen_output(param))); } -void add_null_output(r_cfg_t *cfg, char *param) { +void add_null_output(r_cfg_t* cfg, char* param) { UNUSED(param); list_push(&cfg->output_handler, NULL); } -void add_rtltcp_output(r_cfg_t *cfg, char *param) { - char const *host = "localhost"; - char const *port = "1234"; - char const *extra = hostport_param(param, &host, &port); +void add_rtltcp_output(r_cfg_t* cfg, char* param) { + char const* host = "localhost"; + char const* port = "1234"; + char const* extra = hostport_param(param, &host, &port); if (extra && *extra) { print_logf(LOG_FATAL, "rtl_tcp server", "Unknown parameters \"%s\"", extra); } - print_logf(LOG_CRITICAL, "rtl_tcp server", - "Starting rtl_tcp server at %s port %s", host, port); + print_logf(LOG_CRITICAL, "rtl_tcp server", "Starting rtl_tcp server at %s port %s", host, port); - list_push(&cfg->raw_handler, - raw_output_rtltcp_create(host, port, extra, cfg)); + list_push(&cfg->raw_handler, raw_output_rtltcp_create(host, port, extra, cfg)); } -*/ -/* -void add_sr_dumper(r_cfg_t *cfg, char const *spec, int overwrite) { +void add_sr_dumper(r_cfg_t* cfg, char const* spec, int overwrite) { // create channels add_dumper(cfg, "U8:LOGIC:logic-1-1", overwrite); add_dumper(cfg, "F32:I:analog-1-4-1", overwrite); @@ -1240,23 +1223,23 @@ void add_sr_dumper(r_cfg_t *cfg, char const *spec, int overwrite) { cfg->sr_execopen = overwrite; } -void close_dumpers(struct r_cfg *cfg) { - for (void **iter = cfg->demod->dumper.elems; iter && *iter; ++iter) { - file_info_t *dumper = *iter; +void close_dumpers(struct r_cfg* cfg) { + for (void** iter = cfg->demod->dumper.elems; iter && *iter; ++iter) { + file_info_t* dumper = *iter; if (dumper->file && (dumper->file != stdout)) { fclose(dumper->file); dumper->file = NULL; } } - char const *labels[] = { + char const* labels[] = { "FRAME", // probe1 - "ASK", // probe2 - "FSK", // probe3 - "I", // analog4 - "Q", // analog5 - "AM", // analog6 - "FM", // analog7 + "ASK", // probe2 + "FSK", // probe3 + "I", // analog4 + "Q", // analog5 + "AM", // analog6 + "FM", // analog7 }; if (cfg->sr_filename) { write_sigrok(cfg->sr_filename, cfg->samp_rate, 3, 4, labels); @@ -1266,24 +1249,25 @@ void close_dumpers(struct r_cfg *cfg) { } } -void add_dumper(r_cfg_t *cfg, char const *spec, int overwrite) { +void add_dumper(r_cfg_t* cfg, char const* spec, int overwrite) { size_t spec_len = strlen(spec); if (spec_len >= 3 && !strcmp(&spec[spec_len - 3], ".sr")) { add_sr_dumper(cfg, spec, overwrite); return; } - file_info_t *dumper = calloc(1, sizeof(*dumper)); + file_info_t* dumper = calloc(1, sizeof(*dumper)); if (!dumper) FATAL_CALLOC("add_dumper()"); list_push(&cfg->demod->dumper, dumper); file_info_parse_filename(dumper, spec); - if (strcmp(dumper->path, "-") == 0) { + if (strcmp(dumper->path, "-") == 0) { /* Write samples to stdout */ +/* dumper->file = stdout; -//#ifdef _WIN32 -// _setmode(_fileno(stdin), _O_BINARY); -//#endif +#ifdef _WIN32 + _setmode(_fileno(stdin), _O_BINARY); +#endif } else { if (access(dumper->path, F_OK) == 0 && !overwrite) { fprintf(stderr, "Output file %s already exists, exiting\n", spec); @@ -1303,11 +1287,11 @@ void add_dumper(r_cfg_t *cfg, char const *spec, int overwrite) { } } -void add_infile(r_cfg_t *cfg, char *in_file) { +void add_infile(r_cfg_t* cfg, char* in_file) { list_push(&cfg->in_files, in_file); } -void add_data_tag(struct r_cfg *cfg, char *param) { +void add_data_tag(struct r_cfg* cfg, char* param) { list_push(&cfg->data_tags, data_tag_create(param, get_mgr(cfg))); } */ \ No newline at end of file diff --git a/src/rtl_433/r_util.c b/src/rtl_433/r_util.c index 85eb3220..8b8b58e6 100644 --- a/src/rtl_433/r_util.c +++ b/src/rtl_433/r_util.c @@ -47,7 +47,7 @@ char *format_time_str(char *buf, char const *format, int with_tz, time_t time_se if (with_tz) { strftime(buf + l, LOCAL_TIME_BUFLEN - l, "%z", &tm_info); if (!strcmp(buf + l, "+0000")) - strcpy(buf + l, "Z"); + strcpy(buf + l, "Z"); // NOLINT } return buf; } @@ -77,7 +77,7 @@ char *usecs_time_str(char *buf, char const *format, int with_tz, struct timeval if (with_tz) { strftime(buf + l, LOCAL_TIME_BUFLEN - l, "%z", &tm_info); if (!strcmp(buf + l, "+0000")) - strcpy(buf + l, "Z"); + strcpy(buf + l, "Z"); // NOLINT } return buf; } @@ -146,6 +146,12 @@ float inhg2hpa(float inhg) bool str_endswith(char const *restrict str, char const *restrict suffix) { + if (!suffix) { + return true; + } + if (!str) { + return false; + } int str_len = strlen(str); int suffix_len = strlen(suffix); @@ -198,10 +204,10 @@ char *str_replace(char const *orig, char const *rep, char const *with) ins = strstr(orig, rep); len_front = ins - orig; tmp = strncpy(tmp, orig, len_front) + len_front; - tmp = strcpy(tmp, with) + len_with; + tmp = strcpy(tmp, with) + len_with; // NOLINT orig += len_front + len_rep; // move to next "end of rep" } - strcpy(tmp, orig); + strcpy(tmp, orig); // NOLINT return result; } diff --git a/src/signalDecoder.cpp b/src/signalDecoder.cpp index f78d8511..de1d5d38 100644 --- a/src/signalDecoder.cpp +++ b/src/signalDecoder.cpp @@ -81,250 +81,257 @@ void rtlSetup() { #endif #ifndef MY_DEVICES - // This is a generated fragment from tools/update_rtl_433_devices.sh - - if (rtl_433_ESP::ookModulation) { - memcpy(&cfg->devices[0], &abmt, sizeof(r_device)); - memcpy(&cfg->devices[1], &acurite_rain_896, sizeof(r_device)); - memcpy(&cfg->devices[2], &acurite_th, sizeof(r_device)); - memcpy(&cfg->devices[3], &acurite_txr, sizeof(r_device)); - memcpy(&cfg->devices[4], &acurite_986, sizeof(r_device)); - memcpy(&cfg->devices[5], &acurite_606, sizeof(r_device)); - memcpy(&cfg->devices[6], &acurite_00275rm, sizeof(r_device)); - memcpy(&cfg->devices[7], &acurite_590tx, sizeof(r_device)); - memcpy(&cfg->devices[8], &acurite_01185m, sizeof(r_device)); - memcpy(&cfg->devices[9], &akhan_100F14, sizeof(r_device)); - memcpy(&cfg->devices[10], &alectov1, sizeof(r_device)); - memcpy(&cfg->devices[11], &ambient_weather, sizeof(r_device)); - memcpy(&cfg->devices[12], &ambientweather_tx8300, sizeof(r_device)); - memcpy(&cfg->devices[13], &atech_ws308, sizeof(r_device)); - memcpy(&cfg->devices[14], &auriol_4ld5661, sizeof(r_device)); - memcpy(&cfg->devices[15], &auriol_aft77b2, sizeof(r_device)); - memcpy(&cfg->devices[16], &auriol_afw2a1, sizeof(r_device)); - memcpy(&cfg->devices[17], &auriol_ahfl, sizeof(r_device)); - memcpy(&cfg->devices[18], &auriol_hg02832, sizeof(r_device)); - memcpy(&cfg->devices[19], &baldr_rain, sizeof(r_device)); - memcpy(&cfg->devices[20], &blyss, sizeof(r_device)); - memcpy(&cfg->devices[21], &brennenstuhl_rcs_2044, sizeof(r_device)); - memcpy(&cfg->devices[22], &bresser_3ch, sizeof(r_device)); - memcpy(&cfg->devices[23], &bt_rain, sizeof(r_device)); - memcpy(&cfg->devices[24], &burnhardbbq, sizeof(r_device)); - memcpy(&cfg->devices[25], &calibeur_RF104, sizeof(r_device)); - memcpy(&cfg->devices[26], &cardin, sizeof(r_device)); - memcpy(&cfg->devices[27], &celsia_czc1, sizeof(r_device)); - memcpy(&cfg->devices[28], &chuango, sizeof(r_device)); - memcpy(&cfg->devices[29], &cmr113, sizeof(r_device)); - memcpy(&cfg->devices[30], &companion_wtr001, sizeof(r_device)); - memcpy(&cfg->devices[31], &cotech_36_7959, sizeof(r_device)); - memcpy(&cfg->devices[32], &digitech_xc0324, sizeof(r_device)); - memcpy(&cfg->devices[33], &dish_remote_6_3, sizeof(r_device)); - memcpy(&cfg->devices[34], &dsc_security, sizeof(r_device)); - memcpy(&cfg->devices[35], &dsc_security_ws4945, sizeof(r_device)); - memcpy(&cfg->devices[36], &ecowitt, sizeof(r_device)); - memcpy(&cfg->devices[37], &eurochron_efth800, sizeof(r_device)); - memcpy(&cfg->devices[38], &elro_db286a, sizeof(r_device)); - memcpy(&cfg->devices[39], &elv_em1000, sizeof(r_device)); - memcpy(&cfg->devices[40], &elv_ws2000, sizeof(r_device)); - memcpy(&cfg->devices[41], &emos_e6016, sizeof(r_device)); - memcpy(&cfg->devices[42], &emos_e6016_rain, sizeof(r_device)); - memcpy(&cfg->devices[43], &enocean_erp1, sizeof(r_device)); - memcpy(&cfg->devices[44], &ert_idm, sizeof(r_device)); - memcpy(&cfg->devices[45], &ert_netidm, sizeof(r_device)); - memcpy(&cfg->devices[46], &ert_scm, sizeof(r_device)); - memcpy(&cfg->devices[47], &esa_energy, sizeof(r_device)); - memcpy(&cfg->devices[48], &esperanza_ews, sizeof(r_device)); - memcpy(&cfg->devices[49], &eurochron, sizeof(r_device)); - memcpy(&cfg->devices[50], &fineoffset_WH2, sizeof(r_device)); - memcpy(&cfg->devices[51], &fineoffset_WH0530, sizeof(r_device)); - memcpy(&cfg->devices[52], &fineoffset_wh1050, sizeof(r_device)); - memcpy(&cfg->devices[53], &fineoffset_wh1080, sizeof(r_device)); - memcpy(&cfg->devices[54], &fordremote, sizeof(r_device)); - memcpy(&cfg->devices[55], &fs20, sizeof(r_device)); - memcpy(&cfg->devices[56], &ft004b, sizeof(r_device)); - memcpy(&cfg->devices[57], &funkbus_remote, sizeof(r_device)); - memcpy(&cfg->devices[58], &gasmate_ba1008, sizeof(r_device)); - memcpy(&cfg->devices[59], &generic_motion, sizeof(r_device)); - memcpy(&cfg->devices[60], &generic_remote, sizeof(r_device)); - memcpy(&cfg->devices[61], &generic_temperature_sensor, sizeof(r_device)); - memcpy(&cfg->devices[62], &govee, sizeof(r_device)); - memcpy(&cfg->devices[63], &govee_h5054, sizeof(r_device)); - memcpy(&cfg->devices[64], >_tmbbq05, sizeof(r_device)); - memcpy(&cfg->devices[65], >_wt_02, sizeof(r_device)); - memcpy(&cfg->devices[66], >_wt_03, sizeof(r_device)); - memcpy(&cfg->devices[67], &hcs200, sizeof(r_device)); - memcpy(&cfg->devices[68], &hideki_ts04, sizeof(r_device)); - memcpy(&cfg->devices[69], &honeywell, sizeof(r_device)); - memcpy(&cfg->devices[70], &honeywell_wdb, sizeof(r_device)); - memcpy(&cfg->devices[71], &ht680, sizeof(r_device)); - memcpy(&cfg->devices[72], &ibis_beacon, sizeof(r_device)); - memcpy(&cfg->devices[73], &infactory, sizeof(r_device)); - memcpy(&cfg->devices[74], &kw9015b, sizeof(r_device)); - memcpy(&cfg->devices[75], &interlogix, sizeof(r_device)); - memcpy(&cfg->devices[76], &intertechno, sizeof(r_device)); - memcpy(&cfg->devices[77], &jasco, sizeof(r_device)); - memcpy(&cfg->devices[78], &kedsum, sizeof(r_device)); - memcpy(&cfg->devices[79], &kerui, sizeof(r_device)); - memcpy(&cfg->devices[80], &klimalogg, sizeof(r_device)); - memcpy(&cfg->devices[81], &lacrossetx, sizeof(r_device)); - memcpy(&cfg->devices[82], &lacrosse_tx141x, sizeof(r_device)); - memcpy(&cfg->devices[83], &lacrosse_ws7000, sizeof(r_device)); - memcpy(&cfg->devices[84], &lacrossews, sizeof(r_device)); - memcpy(&cfg->devices[85], &lightwave_rf, sizeof(r_device)); - memcpy(&cfg->devices[86], &markisol, sizeof(r_device)); - memcpy(&cfg->devices[87], &maverick_et73, sizeof(r_device)); - memcpy(&cfg->devices[88], &maverick_et73x, sizeof(r_device)); - memcpy(&cfg->devices[89], &mebus433, sizeof(r_device)); - memcpy(&cfg->devices[90], &megacode, sizeof(r_device)); - memcpy(&cfg->devices[91], &missil_ml0757, sizeof(r_device)); - memcpy(&cfg->devices[92], &neptune_r900, sizeof(r_device)); - memcpy(&cfg->devices[93], &new_template, sizeof(r_device)); - memcpy(&cfg->devices[94], &newkaku, sizeof(r_device)); - memcpy(&cfg->devices[95], &nexa, sizeof(r_device)); - memcpy(&cfg->devices[96], &nexus, sizeof(r_device)); - memcpy(&cfg->devices[97], &nice_flor_s, sizeof(r_device)); - memcpy(&cfg->devices[98], &norgo, sizeof(r_device)); - memcpy(&cfg->devices[99], &oil_standard_ask, sizeof(r_device)); - memcpy(&cfg->devices[100], &opus_xt300, sizeof(r_device)); - memcpy(&cfg->devices[101], &oregon_scientific, sizeof(r_device)); - memcpy(&cfg->devices[102], &oregon_scientific_sl109h, sizeof(r_device)); - memcpy(&cfg->devices[103], &oregon_scientific_v1, sizeof(r_device)); - memcpy(&cfg->devices[104], &philips_aj3650, sizeof(r_device)); - memcpy(&cfg->devices[105], &philips_aj7010, sizeof(r_device)); - memcpy(&cfg->devices[106], &proflame2, sizeof(r_device)); - memcpy(&cfg->devices[107], &prologue, sizeof(r_device)); - memcpy(&cfg->devices[108], &proove, sizeof(r_device)); - memcpy(&cfg->devices[109], &quhwa, sizeof(r_device)); - memcpy(&cfg->devices[110], &radiohead_ask, sizeof(r_device)); - memcpy(&cfg->devices[111], &sensible_living, sizeof(r_device)); - memcpy(&cfg->devices[112], &rainpoint, sizeof(r_device)); - memcpy(&cfg->devices[113], ®ency_fan, sizeof(r_device)); - memcpy(&cfg->devices[114], &revolt_nc5462, sizeof(r_device)); - memcpy(&cfg->devices[115], &rftech, sizeof(r_device)); - memcpy(&cfg->devices[116], &rubicson, sizeof(r_device)); - memcpy(&cfg->devices[117], &rubicson_48659, sizeof(r_device)); - memcpy(&cfg->devices[118], &rubicson_pool_48942, sizeof(r_device)); - memcpy(&cfg->devices[119], &s3318p, sizeof(r_device)); - memcpy(&cfg->devices[120], &schraeder, sizeof(r_device)); - memcpy(&cfg->devices[121], &schrader_EG53MA4, sizeof(r_device)); - memcpy(&cfg->devices[122], &schrader_SMD3MA4, sizeof(r_device)); - memcpy(&cfg->devices[123], &scmplus, sizeof(r_device)); - memcpy(&cfg->devices[124], &secplus_v1, sizeof(r_device)); - memcpy(&cfg->devices[125], &silvercrest, sizeof(r_device)); - memcpy(&cfg->devices[126], &ss_sensor, sizeof(r_device)); - memcpy(&cfg->devices[127], &skylink_motion, sizeof(r_device)); - memcpy(&cfg->devices[128], &smoke_gs558, sizeof(r_device)); - memcpy(&cfg->devices[129], &solight_te44, sizeof(r_device)); - memcpy(&cfg->devices[130], &somfy_rts, sizeof(r_device)); - memcpy(&cfg->devices[131], &springfield, sizeof(r_device)); - memcpy(&cfg->devices[132], &telldus_ft0385r, sizeof(r_device)); - memcpy(&cfg->devices[133], &tfa_30_3221, sizeof(r_device)); - memcpy(&cfg->devices[134], &tfa_drop_303233, sizeof(r_device)); - memcpy(&cfg->devices[135], &tfa_pool_thermometer, sizeof(r_device)); - memcpy(&cfg->devices[136], &tfa_twin_plus_303049, sizeof(r_device)); - memcpy(&cfg->devices[137], &thermopro_tp11, sizeof(r_device)); - memcpy(&cfg->devices[138], &thermopro_tp12, sizeof(r_device)); - memcpy(&cfg->devices[139], &thermopro_tx2, sizeof(r_device)); - memcpy(&cfg->devices[140], &tpms_eezrv, sizeof(r_device)); - memcpy(&cfg->devices[141], &tpms_tyreguard400, sizeof(r_device)); - memcpy(&cfg->devices[142], &ts_ft002, sizeof(r_device)); - memcpy(&cfg->devices[143], &ttx201, sizeof(r_device)); - memcpy(&cfg->devices[144], &vaillant_vrt340f, sizeof(r_device)); - memcpy(&cfg->devices[145], &vauno_en8822c, sizeof(r_device)); - memcpy(&cfg->devices[146], &visonic_powercode, sizeof(r_device)); - memcpy(&cfg->devices[147], &waveman, sizeof(r_device)); - memcpy(&cfg->devices[148], &wec2103, sizeof(r_device)); - memcpy(&cfg->devices[149], &wg_pb12v1, sizeof(r_device)); - memcpy(&cfg->devices[150], &ws2032, sizeof(r_device)); - memcpy(&cfg->devices[151], &wssensor, sizeof(r_device)); - memcpy(&cfg->devices[152], &wt1024, sizeof(r_device)); - memcpy(&cfg->devices[153], &wt450, sizeof(r_device)); - memcpy(&cfg->devices[154], &X10_RF, sizeof(r_device)); - memcpy(&cfg->devices[155], &x10_sec, sizeof(r_device)); - memcpy(&cfg->devices[156], &yale_hsa, sizeof(r_device)); - } else { - memcpy(&cfg->devices[0], &ambientweather_wh31e, sizeof(r_device)); - memcpy(&cfg->devices[1], &ant_antplus, sizeof(r_device)); - memcpy(&cfg->devices[2], &archos_tbh, sizeof(r_device)); - memcpy(&cfg->devices[3], &badger_orion, sizeof(r_device)); - memcpy(&cfg->devices[4], &bresser_5in1, sizeof(r_device)); - memcpy(&cfg->devices[5], &bresser_6in1, sizeof(r_device)); - memcpy(&cfg->devices[6], &bresser_7in1, sizeof(r_device)); - memcpy(&cfg->devices[7], &cavius, sizeof(r_device)); - memcpy(&cfg->devices[8], &ced7000, sizeof(r_device)); - memcpy(&cfg->devices[9], ¤t_cost, sizeof(r_device)); - memcpy(&cfg->devices[10], &danfoss_CFR, sizeof(r_device)); - memcpy(&cfg->devices[11], &directv, sizeof(r_device)); - memcpy(&cfg->devices[12], &ecodhome, sizeof(r_device)); - memcpy(&cfg->devices[13], &efergy_e2_classic, sizeof(r_device)); - memcpy(&cfg->devices[14], &efergy_optical, sizeof(r_device)); - memcpy(&cfg->devices[15], &emax, sizeof(r_device)); - memcpy(&cfg->devices[16], &emontx, sizeof(r_device)); - memcpy(&cfg->devices[17], &esic_emt7110, sizeof(r_device)); - memcpy(&cfg->devices[18], &fineoffset_WH25, sizeof(r_device)); - memcpy(&cfg->devices[19], &fineoffset_WH51, sizeof(r_device)); - memcpy(&cfg->devices[20], &fineoffset_wh1080_fsk, sizeof(r_device)); - memcpy(&cfg->devices[21], &fineoffset_wh31l, sizeof(r_device)); - memcpy(&cfg->devices[22], &fineoffset_wh45, sizeof(r_device)); - memcpy(&cfg->devices[23], &fineoffset_wn34, sizeof(r_device)); - memcpy(&cfg->devices[24], &fineoffset_ws80, sizeof(r_device)); - memcpy(&cfg->devices[25], &flowis, sizeof(r_device)); - memcpy(&cfg->devices[26], &ge_coloreffects, sizeof(r_device)); - memcpy(&cfg->devices[27], &geo_minim, sizeof(r_device)); - memcpy(&cfg->devices[28], &hcs200_fsk, sizeof(r_device)); - memcpy(&cfg->devices[29], &holman_ws5029pcm, sizeof(r_device)); - memcpy(&cfg->devices[30], &holman_ws5029pwm, sizeof(r_device)); - memcpy(&cfg->devices[31], &hondaremote, sizeof(r_device)); - memcpy(&cfg->devices[32], &honeywell_cm921, sizeof(r_device)); - memcpy(&cfg->devices[33], &honeywell_wdb_fsk, sizeof(r_device)); - memcpy(&cfg->devices[34], &ikea_sparsnas, sizeof(r_device)); - memcpy(&cfg->devices[35], &inkbird_ith20r, sizeof(r_device)); - memcpy(&cfg->devices[36], &insteon, sizeof(r_device)); - memcpy(&cfg->devices[37], &lacrosse_breezepro, sizeof(r_device)); - memcpy(&cfg->devices[38], &lacrosse_r1, sizeof(r_device)); - memcpy(&cfg->devices[39], &lacrosse_th3, sizeof(r_device)); - memcpy(&cfg->devices[40], &lacrosse_tx31u, sizeof(r_device)); - memcpy(&cfg->devices[41], &lacrosse_tx34, sizeof(r_device)); - memcpy(&cfg->devices[42], &lacrosse_tx29, sizeof(r_device)); - memcpy(&cfg->devices[43], &lacrosse_tx35, sizeof(r_device)); - memcpy(&cfg->devices[44], &lacrosse_wr1, sizeof(r_device)); - memcpy(&cfg->devices[45], &m_bus_mode_c_t, sizeof(r_device)); - memcpy(&cfg->devices[46], &m_bus_mode_c_t_downlink, sizeof(r_device)); - memcpy(&cfg->devices[47], &m_bus_mode_s, sizeof(r_device)); - memcpy(&cfg->devices[48], &m_bus_mode_r, sizeof(r_device)); - memcpy(&cfg->devices[49], &m_bus_mode_f, sizeof(r_device)); - memcpy(&cfg->devices[50], &marlec_solar, sizeof(r_device)); - memcpy(&cfg->devices[51], &maverick_xr30, sizeof(r_device)); - memcpy(&cfg->devices[52], &oil_smart, sizeof(r_device)); - memcpy(&cfg->devices[53], &oil_standard, sizeof(r_device)); - memcpy(&cfg->devices[54], &oil_watchman, sizeof(r_device)); - memcpy(&cfg->devices[55], &oil_watchman_advanced, sizeof(r_device)); - memcpy(&cfg->devices[56], &rojaflex, sizeof(r_device)); - memcpy(&cfg->devices[57], &sharp_spc775, sizeof(r_device)); - memcpy(&cfg->devices[58], &simplisafe_gen3, sizeof(r_device)); - memcpy(&cfg->devices[59], &somfy_iohc, sizeof(r_device)); - memcpy(&cfg->devices[60], &srsmith_pool_srs_2c_tx, sizeof(r_device)); - memcpy(&cfg->devices[61], &steelmate, sizeof(r_device)); - memcpy(&cfg->devices[62], &tfa_14_1504_v2, sizeof(r_device)); - memcpy(&cfg->devices[63], &tfa_303196, sizeof(r_device)); - memcpy(&cfg->devices[64], &tfa_marbella, sizeof(r_device)); - memcpy(&cfg->devices[65], &tpms_abarth124, sizeof(r_device)); - memcpy(&cfg->devices[66], &tpms_ave, sizeof(r_device)); - memcpy(&cfg->devices[67], &tpms_citroen, sizeof(r_device)); - memcpy(&cfg->devices[68], &tpms_elantra2012, sizeof(r_device)); - memcpy(&cfg->devices[69], &tpms_ford, sizeof(r_device)); - memcpy(&cfg->devices[70], &tpms_hyundai_vdo, sizeof(r_device)); - memcpy(&cfg->devices[71], &tpms_jansite, sizeof(r_device)); - memcpy(&cfg->devices[72], &tpms_jansite_solar, sizeof(r_device)); - memcpy(&cfg->devices[73], &tpms_kia, sizeof(r_device)); - memcpy(&cfg->devices[74], &tpms_pmv107j, sizeof(r_device)); - memcpy(&cfg->devices[75], &tpms_porsche, sizeof(r_device)); - memcpy(&cfg->devices[76], &tpms_renault, sizeof(r_device)); - memcpy(&cfg->devices[77], &tpms_renault_0435r, sizeof(r_device)); - memcpy(&cfg->devices[78], &tpms_toyota, sizeof(r_device)); - memcpy(&cfg->devices[79], &tpms_truck, sizeof(r_device)); - } + // This is a generated fragment from tools/update_rtl_433_devices.sh + +if (rtl_433_ESP::ookModulation) { + memcpy(&cfg->devices[0], &abmt, sizeof(r_device)); + memcpy(&cfg->devices[1], &acurite_rain_896, sizeof(r_device)); + memcpy(&cfg->devices[2], &acurite_th, sizeof(r_device)); + memcpy(&cfg->devices[3], &acurite_txr, sizeof(r_device)); + memcpy(&cfg->devices[4], &acurite_986, sizeof(r_device)); + memcpy(&cfg->devices[5], &acurite_606, sizeof(r_device)); + memcpy(&cfg->devices[6], &acurite_00275rm, sizeof(r_device)); + memcpy(&cfg->devices[7], &acurite_590tx, sizeof(r_device)); + memcpy(&cfg->devices[8], &acurite_01185m, sizeof(r_device)); + memcpy(&cfg->devices[9], &akhan_100F14, sizeof(r_device)); + memcpy(&cfg->devices[10], &alectov1, sizeof(r_device)); + memcpy(&cfg->devices[11], &ambient_weather, sizeof(r_device)); + memcpy(&cfg->devices[12], &ambientweather_tx8300, sizeof(r_device)); + memcpy(&cfg->devices[13], &atech_ws308, sizeof(r_device)); + memcpy(&cfg->devices[14], &auriol_4ld5661, sizeof(r_device)); + memcpy(&cfg->devices[15], &auriol_aft77b2, sizeof(r_device)); + memcpy(&cfg->devices[16], &auriol_afw2a1, sizeof(r_device)); + memcpy(&cfg->devices[17], &auriol_ahfl, sizeof(r_device)); + memcpy(&cfg->devices[18], &auriol_hg02832, sizeof(r_device)); + memcpy(&cfg->devices[19], &baldr_rain, sizeof(r_device)); + memcpy(&cfg->devices[20], &blyss, sizeof(r_device)); + memcpy(&cfg->devices[21], &brennenstuhl_rcs_2044, sizeof(r_device)); + memcpy(&cfg->devices[22], &bresser_3ch, sizeof(r_device)); + memcpy(&cfg->devices[23], &bt_rain, sizeof(r_device)); + memcpy(&cfg->devices[24], &burnhardbbq, sizeof(r_device)); + memcpy(&cfg->devices[25], &calibeur_RF104, sizeof(r_device)); + memcpy(&cfg->devices[26], &cardin, sizeof(r_device)); + memcpy(&cfg->devices[27], &celsia_czc1, sizeof(r_device)); + memcpy(&cfg->devices[28], &chuango, sizeof(r_device)); + memcpy(&cfg->devices[29], &cmr113, sizeof(r_device)); + memcpy(&cfg->devices[30], &companion_wtr001, sizeof(r_device)); + memcpy(&cfg->devices[31], &cotech_36_7959, sizeof(r_device)); + memcpy(&cfg->devices[32], &digitech_xc0324, sizeof(r_device)); + memcpy(&cfg->devices[33], &dish_remote_6_3, sizeof(r_device)); + memcpy(&cfg->devices[34], &dsc_security, sizeof(r_device)); + memcpy(&cfg->devices[35], &dsc_security_ws4945, sizeof(r_device)); + memcpy(&cfg->devices[36], &ecowitt, sizeof(r_device)); + memcpy(&cfg->devices[37], &eurochron_efth800, sizeof(r_device)); + memcpy(&cfg->devices[38], &elro_db286a, sizeof(r_device)); + memcpy(&cfg->devices[39], &elv_em1000, sizeof(r_device)); + memcpy(&cfg->devices[40], &elv_ws2000, sizeof(r_device)); + memcpy(&cfg->devices[41], &emos_e6016, sizeof(r_device)); + memcpy(&cfg->devices[42], &emos_e6016_rain, sizeof(r_device)); + memcpy(&cfg->devices[43], &enocean_erp1, sizeof(r_device)); + memcpy(&cfg->devices[44], &ert_idm, sizeof(r_device)); + memcpy(&cfg->devices[45], &ert_netidm, sizeof(r_device)); + memcpy(&cfg->devices[46], &ert_scm, sizeof(r_device)); + memcpy(&cfg->devices[47], &esa_energy, sizeof(r_device)); + memcpy(&cfg->devices[48], &esperanza_ews, sizeof(r_device)); + memcpy(&cfg->devices[49], &eurochron, sizeof(r_device)); + memcpy(&cfg->devices[50], &fineoffset_WH2, sizeof(r_device)); + memcpy(&cfg->devices[51], &fineoffset_WH0530, sizeof(r_device)); + memcpy(&cfg->devices[52], &fineoffset_wh1050, sizeof(r_device)); + memcpy(&cfg->devices[53], &fineoffset_wh1080, sizeof(r_device)); + memcpy(&cfg->devices[54], &fordremote, sizeof(r_device)); + memcpy(&cfg->devices[55], &fs20, sizeof(r_device)); + memcpy(&cfg->devices[56], &ft004b, sizeof(r_device)); + memcpy(&cfg->devices[57], &funkbus_remote, sizeof(r_device)); + memcpy(&cfg->devices[58], &gasmate_ba1008, sizeof(r_device)); + memcpy(&cfg->devices[59], &generic_motion, sizeof(r_device)); + memcpy(&cfg->devices[60], &generic_remote, sizeof(r_device)); + memcpy(&cfg->devices[61], &generic_temperature_sensor, sizeof(r_device)); + memcpy(&cfg->devices[62], &govee, sizeof(r_device)); + memcpy(&cfg->devices[63], &govee_h5054, sizeof(r_device)); + memcpy(&cfg->devices[64], >_tmbbq05, sizeof(r_device)); + memcpy(&cfg->devices[65], >_wt_02, sizeof(r_device)); + memcpy(&cfg->devices[66], >_wt_03, sizeof(r_device)); + memcpy(&cfg->devices[67], &hcs200, sizeof(r_device)); + memcpy(&cfg->devices[68], &hideki_ts04, sizeof(r_device)); + memcpy(&cfg->devices[69], &honeywell, sizeof(r_device)); + memcpy(&cfg->devices[70], &honeywell_wdb, sizeof(r_device)); + memcpy(&cfg->devices[71], &ht680, sizeof(r_device)); + memcpy(&cfg->devices[72], &ibis_beacon, sizeof(r_device)); + memcpy(&cfg->devices[73], &infactory, sizeof(r_device)); + memcpy(&cfg->devices[74], &kw9015b, sizeof(r_device)); + memcpy(&cfg->devices[75], &interlogix, sizeof(r_device)); + memcpy(&cfg->devices[76], &intertechno, sizeof(r_device)); + memcpy(&cfg->devices[77], &jasco, sizeof(r_device)); + memcpy(&cfg->devices[78], &kedsum, sizeof(r_device)); + memcpy(&cfg->devices[79], &kerui, sizeof(r_device)); + memcpy(&cfg->devices[80], &klimalogg, sizeof(r_device)); + memcpy(&cfg->devices[81], &lacrossetx, sizeof(r_device)); + memcpy(&cfg->devices[82], &lacrosse_tx141x, sizeof(r_device)); + memcpy(&cfg->devices[83], &lacrosse_ws7000, sizeof(r_device)); + memcpy(&cfg->devices[84], &lacrossews, sizeof(r_device)); + memcpy(&cfg->devices[85], &lightwave_rf, sizeof(r_device)); + memcpy(&cfg->devices[86], &markisol, sizeof(r_device)); + memcpy(&cfg->devices[87], &maverick_et73, sizeof(r_device)); + memcpy(&cfg->devices[88], &maverick_et73x, sizeof(r_device)); + memcpy(&cfg->devices[89], &mebus433, sizeof(r_device)); + memcpy(&cfg->devices[90], &megacode, sizeof(r_device)); + memcpy(&cfg->devices[91], &missil_ml0757, sizeof(r_device)); + memcpy(&cfg->devices[92], &neptune_r900, sizeof(r_device)); + memcpy(&cfg->devices[93], &new_template, sizeof(r_device)); + memcpy(&cfg->devices[94], &newkaku, sizeof(r_device)); + memcpy(&cfg->devices[95], &nexa, sizeof(r_device)); + memcpy(&cfg->devices[96], &nexus, sizeof(r_device)); + memcpy(&cfg->devices[97], &nice_flor_s, sizeof(r_device)); + memcpy(&cfg->devices[98], &norgo, sizeof(r_device)); + memcpy(&cfg->devices[99], &oil_standard_ask, sizeof(r_device)); + memcpy(&cfg->devices[100], &opus_xt300, sizeof(r_device)); + memcpy(&cfg->devices[101], &oregon_scientific, sizeof(r_device)); + memcpy(&cfg->devices[102], &oregon_scientific_sl109h, sizeof(r_device)); + memcpy(&cfg->devices[103], &oregon_scientific_v1, sizeof(r_device)); + memcpy(&cfg->devices[104], &philips_aj3650, sizeof(r_device)); + memcpy(&cfg->devices[105], &philips_aj7010, sizeof(r_device)); + memcpy(&cfg->devices[106], &proflame2, sizeof(r_device)); + memcpy(&cfg->devices[107], &prologue, sizeof(r_device)); + memcpy(&cfg->devices[108], &proove, sizeof(r_device)); + memcpy(&cfg->devices[109], &quhwa, sizeof(r_device)); + memcpy(&cfg->devices[110], &radiohead_ask, sizeof(r_device)); + memcpy(&cfg->devices[111], &sensible_living, sizeof(r_device)); + memcpy(&cfg->devices[112], &rainpoint, sizeof(r_device)); + memcpy(&cfg->devices[113], ®ency_fan, sizeof(r_device)); + memcpy(&cfg->devices[114], &revolt_nc5462, sizeof(r_device)); + memcpy(&cfg->devices[115], &rftech, sizeof(r_device)); + memcpy(&cfg->devices[116], &rubicson, sizeof(r_device)); + memcpy(&cfg->devices[117], &rubicson_48659, sizeof(r_device)); + memcpy(&cfg->devices[118], &rubicson_pool_48942, sizeof(r_device)); + memcpy(&cfg->devices[119], &s3318p, sizeof(r_device)); + memcpy(&cfg->devices[120], &schou_72543_rain, sizeof(r_device)); + memcpy(&cfg->devices[121], &schraeder, sizeof(r_device)); + memcpy(&cfg->devices[122], &schrader_EG53MA4, sizeof(r_device)); + memcpy(&cfg->devices[123], &schrader_SMD3MA4, sizeof(r_device)); + memcpy(&cfg->devices[124], &scmplus, sizeof(r_device)); + memcpy(&cfg->devices[125], &secplus_v1, sizeof(r_device)); + memcpy(&cfg->devices[126], &silvercrest, sizeof(r_device)); + memcpy(&cfg->devices[127], &ss_sensor, sizeof(r_device)); + memcpy(&cfg->devices[128], &skylink_motion, sizeof(r_device)); + memcpy(&cfg->devices[129], &smoke_gs558, sizeof(r_device)); + memcpy(&cfg->devices[130], &solight_te44, sizeof(r_device)); + memcpy(&cfg->devices[131], &somfy_rts, sizeof(r_device)); + memcpy(&cfg->devices[132], &springfield, sizeof(r_device)); + memcpy(&cfg->devices[133], &telldus_ft0385r, sizeof(r_device)); + memcpy(&cfg->devices[134], &tfa_30_3221, sizeof(r_device)); + memcpy(&cfg->devices[135], &tfa_drop_303233, sizeof(r_device)); + memcpy(&cfg->devices[136], &tfa_pool_thermometer, sizeof(r_device)); + memcpy(&cfg->devices[137], &tfa_twin_plus_303049, sizeof(r_device)); + memcpy(&cfg->devices[138], &thermopro_tp11, sizeof(r_device)); + memcpy(&cfg->devices[139], &thermopro_tp12, sizeof(r_device)); + memcpy(&cfg->devices[140], &thermopro_tx2, sizeof(r_device)); + memcpy(&cfg->devices[141], &thermopro_tx2c, sizeof(r_device)); + memcpy(&cfg->devices[142], &tpms_eezrv, sizeof(r_device)); + memcpy(&cfg->devices[143], &tpms_tyreguard400, sizeof(r_device)); + memcpy(&cfg->devices[144], &ts_ft002, sizeof(r_device)); + memcpy(&cfg->devices[145], &ttx201, sizeof(r_device)); + memcpy(&cfg->devices[146], &vaillant_vrt340f, sizeof(r_device)); + memcpy(&cfg->devices[147], &vauno_en8822c, sizeof(r_device)); + memcpy(&cfg->devices[148], &visonic_powercode, sizeof(r_device)); + memcpy(&cfg->devices[149], &waveman, sizeof(r_device)); + memcpy(&cfg->devices[150], &wec2103, sizeof(r_device)); + memcpy(&cfg->devices[151], &wg_pb12v1, sizeof(r_device)); + memcpy(&cfg->devices[152], &ws2032, sizeof(r_device)); + memcpy(&cfg->devices[153], &wssensor, sizeof(r_device)); + memcpy(&cfg->devices[154], &wt1024, sizeof(r_device)); + memcpy(&cfg->devices[155], &wt450, sizeof(r_device)); + memcpy(&cfg->devices[156], &X10_RF, sizeof(r_device)); + memcpy(&cfg->devices[157], &x10_sec, sizeof(r_device)); + memcpy(&cfg->devices[158], &yale_hsa, sizeof(r_device)); +} else { + memcpy(&cfg->devices[0], &ambientweather_wh31e, sizeof(r_device)); + memcpy(&cfg->devices[1], &ant_antplus, sizeof(r_device)); + memcpy(&cfg->devices[2], &archos_tbh, sizeof(r_device)); + memcpy(&cfg->devices[3], &badger_orion, sizeof(r_device)); + memcpy(&cfg->devices[4], &bresser_5in1, sizeof(r_device)); + memcpy(&cfg->devices[5], &bresser_6in1, sizeof(r_device)); + memcpy(&cfg->devices[6], &bresser_7in1, sizeof(r_device)); + memcpy(&cfg->devices[7], &bresser_leakage, sizeof(r_device)); + memcpy(&cfg->devices[8], &bresser_lightning, sizeof(r_device)); + memcpy(&cfg->devices[9], &cavius, sizeof(r_device)); + memcpy(&cfg->devices[10], &ced7000, sizeof(r_device)); + memcpy(&cfg->devices[11], ¤t_cost, sizeof(r_device)); + memcpy(&cfg->devices[12], &danfoss_CFR, sizeof(r_device)); + memcpy(&cfg->devices[13], &directv, sizeof(r_device)); + memcpy(&cfg->devices[14], &ecodhome, sizeof(r_device)); + memcpy(&cfg->devices[15], &efergy_e2_classic, sizeof(r_device)); + memcpy(&cfg->devices[16], &efergy_optical, sizeof(r_device)); + memcpy(&cfg->devices[17], &emax, sizeof(r_device)); + memcpy(&cfg->devices[18], &emontx, sizeof(r_device)); + memcpy(&cfg->devices[19], &esic_emt7110, sizeof(r_device)); + memcpy(&cfg->devices[20], &fineoffset_WH25, sizeof(r_device)); + memcpy(&cfg->devices[21], &fineoffset_WH51, sizeof(r_device)); + memcpy(&cfg->devices[22], &tfa_303151, sizeof(r_device)); + memcpy(&cfg->devices[23], &fineoffset_wh1080_fsk, sizeof(r_device)); + memcpy(&cfg->devices[24], &fineoffset_wh31l, sizeof(r_device)); + memcpy(&cfg->devices[25], &fineoffset_wh45, sizeof(r_device)); + memcpy(&cfg->devices[26], &fineoffset_wn34, sizeof(r_device)); + memcpy(&cfg->devices[27], &fineoffset_ws80, sizeof(r_device)); + memcpy(&cfg->devices[28], &fineoffset_ws90, sizeof(r_device)); + memcpy(&cfg->devices[29], &flowis, sizeof(r_device)); + memcpy(&cfg->devices[30], &ge_coloreffects, sizeof(r_device)); + memcpy(&cfg->devices[31], &geo_minim, sizeof(r_device)); + memcpy(&cfg->devices[32], &hcs200_fsk, sizeof(r_device)); + memcpy(&cfg->devices[33], &holman_ws5029pcm, sizeof(r_device)); + memcpy(&cfg->devices[34], &holman_ws5029pwm, sizeof(r_device)); + memcpy(&cfg->devices[35], &hondaremote, sizeof(r_device)); + memcpy(&cfg->devices[36], &honeywell_cm921, sizeof(r_device)); + memcpy(&cfg->devices[37], &honeywell_wdb_fsk, sizeof(r_device)); + memcpy(&cfg->devices[38], &ikea_sparsnas, sizeof(r_device)); + memcpy(&cfg->devices[39], &inkbird_ith20r, sizeof(r_device)); + memcpy(&cfg->devices[40], &insteon, sizeof(r_device)); + memcpy(&cfg->devices[41], &lacrosse_breezepro, sizeof(r_device)); + memcpy(&cfg->devices[42], &lacrosse_r1, sizeof(r_device)); + memcpy(&cfg->devices[43], &lacrosse_th3, sizeof(r_device)); + memcpy(&cfg->devices[44], &lacrosse_tx31u, sizeof(r_device)); + memcpy(&cfg->devices[45], &lacrosse_tx34, sizeof(r_device)); + memcpy(&cfg->devices[46], &lacrosse_tx29, sizeof(r_device)); + memcpy(&cfg->devices[47], &lacrosse_tx35, sizeof(r_device)); + memcpy(&cfg->devices[48], &lacrosse_wr1, sizeof(r_device)); + memcpy(&cfg->devices[49], &m_bus_mode_c_t, sizeof(r_device)); + memcpy(&cfg->devices[50], &m_bus_mode_c_t_downlink, sizeof(r_device)); + memcpy(&cfg->devices[51], &m_bus_mode_s, sizeof(r_device)); + memcpy(&cfg->devices[52], &m_bus_mode_r, sizeof(r_device)); + memcpy(&cfg->devices[53], &m_bus_mode_f, sizeof(r_device)); + memcpy(&cfg->devices[54], &marlec_solar, sizeof(r_device)); + memcpy(&cfg->devices[55], &maverick_xr30, sizeof(r_device)); + memcpy(&cfg->devices[56], &oil_smart, sizeof(r_device)); + memcpy(&cfg->devices[57], &oil_standard, sizeof(r_device)); + memcpy(&cfg->devices[58], &oil_watchman, sizeof(r_device)); + memcpy(&cfg->devices[59], &oil_watchman_advanced, sizeof(r_device)); + memcpy(&cfg->devices[60], &rojaflex, sizeof(r_device)); + memcpy(&cfg->devices[61], &sharp_spc775, sizeof(r_device)); + memcpy(&cfg->devices[62], &simplisafe_gen3, sizeof(r_device)); + memcpy(&cfg->devices[63], &somfy_iohc, sizeof(r_device)); + memcpy(&cfg->devices[64], &srsmith_pool_srs_2c_tx, sizeof(r_device)); + memcpy(&cfg->devices[65], &steelmate, sizeof(r_device)); + memcpy(&cfg->devices[66], &tfa_14_1504_v2, sizeof(r_device)); + memcpy(&cfg->devices[67], &tfa_303196, sizeof(r_device)); + memcpy(&cfg->devices[68], &tfa_marbella, sizeof(r_device)); + memcpy(&cfg->devices[69], &tpms_abarth124, sizeof(r_device)); + memcpy(&cfg->devices[70], &tpms_ave, sizeof(r_device)); + memcpy(&cfg->devices[71], &tpms_citroen, sizeof(r_device)); + memcpy(&cfg->devices[72], &tpms_elantra2012, sizeof(r_device)); + memcpy(&cfg->devices[73], &tpms_ford, sizeof(r_device)); + memcpy(&cfg->devices[74], &tpms_hyundai_vdo, sizeof(r_device)); + memcpy(&cfg->devices[75], &tpms_jansite, sizeof(r_device)); + memcpy(&cfg->devices[76], &tpms_jansite_solar, sizeof(r_device)); + memcpy(&cfg->devices[77], &tpms_kia, sizeof(r_device)); + memcpy(&cfg->devices[78], &tpms_nissan, sizeof(r_device)); + memcpy(&cfg->devices[79], &tpms_pmv107j, sizeof(r_device)); + memcpy(&cfg->devices[80], &tpms_porsche, sizeof(r_device)); + memcpy(&cfg->devices[81], &tpms_renault, sizeof(r_device)); + memcpy(&cfg->devices[82], &tpms_renault_0435r, sizeof(r_device)); + memcpy(&cfg->devices[83], &tpms_toyota, sizeof(r_device)); + memcpy(&cfg->devices[84], &tpms_truck, sizeof(r_device)); +} - // end of fragment + // end of fragment #else memcpy(&cfg->devices[0], &lacrosse_tx141x, sizeof(r_device)); @@ -485,7 +492,8 @@ void rtl_433_DecoderTask(void* pvParameters) { } if (events == 0) { #ifdef RTL_ANALYZER - pulse_analyzer(rtl_pulses, rtl_433_ESP::ookModulation ? 1 : 2); + r_device device = {.log_fn = log_device_handler, .output_ctx = cfg}; + pulse_analyzer(rtl_pulses, rtl_433_ESP::ookModulation ? 1 : 2, &device); #endif rtl_433_ESP::unparsedSignals++; #ifdef PUBLISH_UNPARSED diff --git a/tools/decoder.fragment b/tools/decoder.fragment index b073f2f4..0e17851f 100644 --- a/tools/decoder.fragment +++ b/tools/decoder.fragment @@ -121,43 +121,45 @@ if (rtl_433_ESP::ookModulation) { memcpy(&cfg->devices[117], &rubicson_48659, sizeof(r_device)); memcpy(&cfg->devices[118], &rubicson_pool_48942, sizeof(r_device)); memcpy(&cfg->devices[119], &s3318p, sizeof(r_device)); - memcpy(&cfg->devices[120], &schraeder, sizeof(r_device)); - memcpy(&cfg->devices[121], &schrader_EG53MA4, sizeof(r_device)); - memcpy(&cfg->devices[122], &schrader_SMD3MA4, sizeof(r_device)); - memcpy(&cfg->devices[123], &scmplus, sizeof(r_device)); - memcpy(&cfg->devices[124], &secplus_v1, sizeof(r_device)); - memcpy(&cfg->devices[125], &silvercrest, sizeof(r_device)); - memcpy(&cfg->devices[126], &ss_sensor, sizeof(r_device)); - memcpy(&cfg->devices[127], &skylink_motion, sizeof(r_device)); - memcpy(&cfg->devices[128], &smoke_gs558, sizeof(r_device)); - memcpy(&cfg->devices[129], &solight_te44, sizeof(r_device)); - memcpy(&cfg->devices[130], &somfy_rts, sizeof(r_device)); - memcpy(&cfg->devices[131], &springfield, sizeof(r_device)); - memcpy(&cfg->devices[132], &telldus_ft0385r, sizeof(r_device)); - memcpy(&cfg->devices[133], &tfa_30_3221, sizeof(r_device)); - memcpy(&cfg->devices[134], &tfa_drop_303233, sizeof(r_device)); - memcpy(&cfg->devices[135], &tfa_pool_thermometer, sizeof(r_device)); - memcpy(&cfg->devices[136], &tfa_twin_plus_303049, sizeof(r_device)); - memcpy(&cfg->devices[137], &thermopro_tp11, sizeof(r_device)); - memcpy(&cfg->devices[138], &thermopro_tp12, sizeof(r_device)); - memcpy(&cfg->devices[139], &thermopro_tx2, sizeof(r_device)); - memcpy(&cfg->devices[140], &tpms_eezrv, sizeof(r_device)); - memcpy(&cfg->devices[141], &tpms_tyreguard400, sizeof(r_device)); - memcpy(&cfg->devices[142], &ts_ft002, sizeof(r_device)); - memcpy(&cfg->devices[143], &ttx201, sizeof(r_device)); - memcpy(&cfg->devices[144], &vaillant_vrt340f, sizeof(r_device)); - memcpy(&cfg->devices[145], &vauno_en8822c, sizeof(r_device)); - memcpy(&cfg->devices[146], &visonic_powercode, sizeof(r_device)); - memcpy(&cfg->devices[147], &waveman, sizeof(r_device)); - memcpy(&cfg->devices[148], &wec2103, sizeof(r_device)); - memcpy(&cfg->devices[149], &wg_pb12v1, sizeof(r_device)); - memcpy(&cfg->devices[150], &ws2032, sizeof(r_device)); - memcpy(&cfg->devices[151], &wssensor, sizeof(r_device)); - memcpy(&cfg->devices[152], &wt1024, sizeof(r_device)); - memcpy(&cfg->devices[153], &wt450, sizeof(r_device)); - memcpy(&cfg->devices[154], &X10_RF, sizeof(r_device)); - memcpy(&cfg->devices[155], &x10_sec, sizeof(r_device)); - memcpy(&cfg->devices[156], &yale_hsa, sizeof(r_device)); + memcpy(&cfg->devices[120], &schou_72543_rain, sizeof(r_device)); + memcpy(&cfg->devices[121], &schraeder, sizeof(r_device)); + memcpy(&cfg->devices[122], &schrader_EG53MA4, sizeof(r_device)); + memcpy(&cfg->devices[123], &schrader_SMD3MA4, sizeof(r_device)); + memcpy(&cfg->devices[124], &scmplus, sizeof(r_device)); + memcpy(&cfg->devices[125], &secplus_v1, sizeof(r_device)); + memcpy(&cfg->devices[126], &silvercrest, sizeof(r_device)); + memcpy(&cfg->devices[127], &ss_sensor, sizeof(r_device)); + memcpy(&cfg->devices[128], &skylink_motion, sizeof(r_device)); + memcpy(&cfg->devices[129], &smoke_gs558, sizeof(r_device)); + memcpy(&cfg->devices[130], &solight_te44, sizeof(r_device)); + memcpy(&cfg->devices[131], &somfy_rts, sizeof(r_device)); + memcpy(&cfg->devices[132], &springfield, sizeof(r_device)); + memcpy(&cfg->devices[133], &telldus_ft0385r, sizeof(r_device)); + memcpy(&cfg->devices[134], &tfa_30_3221, sizeof(r_device)); + memcpy(&cfg->devices[135], &tfa_drop_303233, sizeof(r_device)); + memcpy(&cfg->devices[136], &tfa_pool_thermometer, sizeof(r_device)); + memcpy(&cfg->devices[137], &tfa_twin_plus_303049, sizeof(r_device)); + memcpy(&cfg->devices[138], &thermopro_tp11, sizeof(r_device)); + memcpy(&cfg->devices[139], &thermopro_tp12, sizeof(r_device)); + memcpy(&cfg->devices[140], &thermopro_tx2, sizeof(r_device)); + memcpy(&cfg->devices[141], &thermopro_tx2c, sizeof(r_device)); + memcpy(&cfg->devices[142], &tpms_eezrv, sizeof(r_device)); + memcpy(&cfg->devices[143], &tpms_tyreguard400, sizeof(r_device)); + memcpy(&cfg->devices[144], &ts_ft002, sizeof(r_device)); + memcpy(&cfg->devices[145], &ttx201, sizeof(r_device)); + memcpy(&cfg->devices[146], &vaillant_vrt340f, sizeof(r_device)); + memcpy(&cfg->devices[147], &vauno_en8822c, sizeof(r_device)); + memcpy(&cfg->devices[148], &visonic_powercode, sizeof(r_device)); + memcpy(&cfg->devices[149], &waveman, sizeof(r_device)); + memcpy(&cfg->devices[150], &wec2103, sizeof(r_device)); + memcpy(&cfg->devices[151], &wg_pb12v1, sizeof(r_device)); + memcpy(&cfg->devices[152], &ws2032, sizeof(r_device)); + memcpy(&cfg->devices[153], &wssensor, sizeof(r_device)); + memcpy(&cfg->devices[154], &wt1024, sizeof(r_device)); + memcpy(&cfg->devices[155], &wt450, sizeof(r_device)); + memcpy(&cfg->devices[156], &X10_RF, sizeof(r_device)); + memcpy(&cfg->devices[157], &x10_sec, sizeof(r_device)); + memcpy(&cfg->devices[158], &yale_hsa, sizeof(r_device)); } else { memcpy(&cfg->devices[0], &ambientweather_wh31e, sizeof(r_device)); memcpy(&cfg->devices[1], &ant_antplus, sizeof(r_device)); @@ -166,79 +168,84 @@ if (rtl_433_ESP::ookModulation) { memcpy(&cfg->devices[4], &bresser_5in1, sizeof(r_device)); memcpy(&cfg->devices[5], &bresser_6in1, sizeof(r_device)); memcpy(&cfg->devices[6], &bresser_7in1, sizeof(r_device)); - memcpy(&cfg->devices[7], &cavius, sizeof(r_device)); - memcpy(&cfg->devices[8], &ced7000, sizeof(r_device)); - memcpy(&cfg->devices[9], ¤t_cost, sizeof(r_device)); - memcpy(&cfg->devices[10], &danfoss_CFR, sizeof(r_device)); - memcpy(&cfg->devices[11], &directv, sizeof(r_device)); - memcpy(&cfg->devices[12], &ecodhome, sizeof(r_device)); - memcpy(&cfg->devices[13], &efergy_e2_classic, sizeof(r_device)); - memcpy(&cfg->devices[14], &efergy_optical, sizeof(r_device)); - memcpy(&cfg->devices[15], &emax, sizeof(r_device)); - memcpy(&cfg->devices[16], &emontx, sizeof(r_device)); - memcpy(&cfg->devices[17], &esic_emt7110, sizeof(r_device)); - memcpy(&cfg->devices[18], &fineoffset_WH25, sizeof(r_device)); - memcpy(&cfg->devices[19], &fineoffset_WH51, sizeof(r_device)); - memcpy(&cfg->devices[20], &fineoffset_wh1080_fsk, sizeof(r_device)); - memcpy(&cfg->devices[21], &fineoffset_wh31l, sizeof(r_device)); - memcpy(&cfg->devices[22], &fineoffset_wh45, sizeof(r_device)); - memcpy(&cfg->devices[23], &fineoffset_wn34, sizeof(r_device)); - memcpy(&cfg->devices[24], &fineoffset_ws80, sizeof(r_device)); - memcpy(&cfg->devices[25], &flowis, sizeof(r_device)); - memcpy(&cfg->devices[26], &ge_coloreffects, sizeof(r_device)); - memcpy(&cfg->devices[27], &geo_minim, sizeof(r_device)); - memcpy(&cfg->devices[28], &hcs200_fsk, sizeof(r_device)); - memcpy(&cfg->devices[29], &holman_ws5029pcm, sizeof(r_device)); - memcpy(&cfg->devices[30], &holman_ws5029pwm, sizeof(r_device)); - memcpy(&cfg->devices[31], &hondaremote, sizeof(r_device)); - memcpy(&cfg->devices[32], &honeywell_cm921, sizeof(r_device)); - memcpy(&cfg->devices[33], &honeywell_wdb_fsk, sizeof(r_device)); - memcpy(&cfg->devices[34], &ikea_sparsnas, sizeof(r_device)); - memcpy(&cfg->devices[35], &inkbird_ith20r, sizeof(r_device)); - memcpy(&cfg->devices[36], &insteon, sizeof(r_device)); - memcpy(&cfg->devices[37], &lacrosse_breezepro, sizeof(r_device)); - memcpy(&cfg->devices[38], &lacrosse_r1, sizeof(r_device)); - memcpy(&cfg->devices[39], &lacrosse_th3, sizeof(r_device)); - memcpy(&cfg->devices[40], &lacrosse_tx31u, sizeof(r_device)); - memcpy(&cfg->devices[41], &lacrosse_tx34, sizeof(r_device)); - memcpy(&cfg->devices[42], &lacrosse_tx29, sizeof(r_device)); - memcpy(&cfg->devices[43], &lacrosse_tx35, sizeof(r_device)); - memcpy(&cfg->devices[44], &lacrosse_wr1, sizeof(r_device)); - memcpy(&cfg->devices[45], &m_bus_mode_c_t, sizeof(r_device)); - memcpy(&cfg->devices[46], &m_bus_mode_c_t_downlink, sizeof(r_device)); - memcpy(&cfg->devices[47], &m_bus_mode_s, sizeof(r_device)); - memcpy(&cfg->devices[48], &m_bus_mode_r, sizeof(r_device)); - memcpy(&cfg->devices[49], &m_bus_mode_f, sizeof(r_device)); - memcpy(&cfg->devices[50], &marlec_solar, sizeof(r_device)); - memcpy(&cfg->devices[51], &maverick_xr30, sizeof(r_device)); - memcpy(&cfg->devices[52], &oil_smart, sizeof(r_device)); - memcpy(&cfg->devices[53], &oil_standard, sizeof(r_device)); - memcpy(&cfg->devices[54], &oil_watchman, sizeof(r_device)); - memcpy(&cfg->devices[55], &oil_watchman_advanced, sizeof(r_device)); - memcpy(&cfg->devices[56], &rojaflex, sizeof(r_device)); - memcpy(&cfg->devices[57], &sharp_spc775, sizeof(r_device)); - memcpy(&cfg->devices[58], &simplisafe_gen3, sizeof(r_device)); - memcpy(&cfg->devices[59], &somfy_iohc, sizeof(r_device)); - memcpy(&cfg->devices[60], &srsmith_pool_srs_2c_tx, sizeof(r_device)); - memcpy(&cfg->devices[61], &steelmate, sizeof(r_device)); - memcpy(&cfg->devices[62], &tfa_14_1504_v2, sizeof(r_device)); - memcpy(&cfg->devices[63], &tfa_303196, sizeof(r_device)); - memcpy(&cfg->devices[64], &tfa_marbella, sizeof(r_device)); - memcpy(&cfg->devices[65], &tpms_abarth124, sizeof(r_device)); - memcpy(&cfg->devices[66], &tpms_ave, sizeof(r_device)); - memcpy(&cfg->devices[67], &tpms_citroen, sizeof(r_device)); - memcpy(&cfg->devices[68], &tpms_elantra2012, sizeof(r_device)); - memcpy(&cfg->devices[69], &tpms_ford, sizeof(r_device)); - memcpy(&cfg->devices[70], &tpms_hyundai_vdo, sizeof(r_device)); - memcpy(&cfg->devices[71], &tpms_jansite, sizeof(r_device)); - memcpy(&cfg->devices[72], &tpms_jansite_solar, sizeof(r_device)); - memcpy(&cfg->devices[73], &tpms_kia, sizeof(r_device)); - memcpy(&cfg->devices[74], &tpms_pmv107j, sizeof(r_device)); - memcpy(&cfg->devices[75], &tpms_porsche, sizeof(r_device)); - memcpy(&cfg->devices[76], &tpms_renault, sizeof(r_device)); - memcpy(&cfg->devices[77], &tpms_renault_0435r, sizeof(r_device)); - memcpy(&cfg->devices[78], &tpms_toyota, sizeof(r_device)); - memcpy(&cfg->devices[79], &tpms_truck, sizeof(r_device)); + memcpy(&cfg->devices[7], &bresser_leakage, sizeof(r_device)); + memcpy(&cfg->devices[8], &bresser_lightning, sizeof(r_device)); + memcpy(&cfg->devices[9], &cavius, sizeof(r_device)); + memcpy(&cfg->devices[10], &ced7000, sizeof(r_device)); + memcpy(&cfg->devices[11], ¤t_cost, sizeof(r_device)); + memcpy(&cfg->devices[12], &danfoss_CFR, sizeof(r_device)); + memcpy(&cfg->devices[13], &directv, sizeof(r_device)); + memcpy(&cfg->devices[14], &ecodhome, sizeof(r_device)); + memcpy(&cfg->devices[15], &efergy_e2_classic, sizeof(r_device)); + memcpy(&cfg->devices[16], &efergy_optical, sizeof(r_device)); + memcpy(&cfg->devices[17], &emax, sizeof(r_device)); + memcpy(&cfg->devices[18], &emontx, sizeof(r_device)); + memcpy(&cfg->devices[19], &esic_emt7110, sizeof(r_device)); + memcpy(&cfg->devices[20], &fineoffset_WH25, sizeof(r_device)); + memcpy(&cfg->devices[21], &fineoffset_WH51, sizeof(r_device)); + memcpy(&cfg->devices[22], &tfa_303151, sizeof(r_device)); + memcpy(&cfg->devices[23], &fineoffset_wh1080_fsk, sizeof(r_device)); + memcpy(&cfg->devices[24], &fineoffset_wh31l, sizeof(r_device)); + memcpy(&cfg->devices[25], &fineoffset_wh45, sizeof(r_device)); + memcpy(&cfg->devices[26], &fineoffset_wn34, sizeof(r_device)); + memcpy(&cfg->devices[27], &fineoffset_ws80, sizeof(r_device)); + memcpy(&cfg->devices[28], &fineoffset_ws90, sizeof(r_device)); + memcpy(&cfg->devices[29], &flowis, sizeof(r_device)); + memcpy(&cfg->devices[30], &ge_coloreffects, sizeof(r_device)); + memcpy(&cfg->devices[31], &geo_minim, sizeof(r_device)); + memcpy(&cfg->devices[32], &hcs200_fsk, sizeof(r_device)); + memcpy(&cfg->devices[33], &holman_ws5029pcm, sizeof(r_device)); + memcpy(&cfg->devices[34], &holman_ws5029pwm, sizeof(r_device)); + memcpy(&cfg->devices[35], &hondaremote, sizeof(r_device)); + memcpy(&cfg->devices[36], &honeywell_cm921, sizeof(r_device)); + memcpy(&cfg->devices[37], &honeywell_wdb_fsk, sizeof(r_device)); + memcpy(&cfg->devices[38], &ikea_sparsnas, sizeof(r_device)); + memcpy(&cfg->devices[39], &inkbird_ith20r, sizeof(r_device)); + memcpy(&cfg->devices[40], &insteon, sizeof(r_device)); + memcpy(&cfg->devices[41], &lacrosse_breezepro, sizeof(r_device)); + memcpy(&cfg->devices[42], &lacrosse_r1, sizeof(r_device)); + memcpy(&cfg->devices[43], &lacrosse_th3, sizeof(r_device)); + memcpy(&cfg->devices[44], &lacrosse_tx31u, sizeof(r_device)); + memcpy(&cfg->devices[45], &lacrosse_tx34, sizeof(r_device)); + memcpy(&cfg->devices[46], &lacrosse_tx29, sizeof(r_device)); + memcpy(&cfg->devices[47], &lacrosse_tx35, sizeof(r_device)); + memcpy(&cfg->devices[48], &lacrosse_wr1, sizeof(r_device)); + memcpy(&cfg->devices[49], &m_bus_mode_c_t, sizeof(r_device)); + memcpy(&cfg->devices[50], &m_bus_mode_c_t_downlink, sizeof(r_device)); + memcpy(&cfg->devices[51], &m_bus_mode_s, sizeof(r_device)); + memcpy(&cfg->devices[52], &m_bus_mode_r, sizeof(r_device)); + memcpy(&cfg->devices[53], &m_bus_mode_f, sizeof(r_device)); + memcpy(&cfg->devices[54], &marlec_solar, sizeof(r_device)); + memcpy(&cfg->devices[55], &maverick_xr30, sizeof(r_device)); + memcpy(&cfg->devices[56], &oil_smart, sizeof(r_device)); + memcpy(&cfg->devices[57], &oil_standard, sizeof(r_device)); + memcpy(&cfg->devices[58], &oil_watchman, sizeof(r_device)); + memcpy(&cfg->devices[59], &oil_watchman_advanced, sizeof(r_device)); + memcpy(&cfg->devices[60], &rojaflex, sizeof(r_device)); + memcpy(&cfg->devices[61], &sharp_spc775, sizeof(r_device)); + memcpy(&cfg->devices[62], &simplisafe_gen3, sizeof(r_device)); + memcpy(&cfg->devices[63], &somfy_iohc, sizeof(r_device)); + memcpy(&cfg->devices[64], &srsmith_pool_srs_2c_tx, sizeof(r_device)); + memcpy(&cfg->devices[65], &steelmate, sizeof(r_device)); + memcpy(&cfg->devices[66], &tfa_14_1504_v2, sizeof(r_device)); + memcpy(&cfg->devices[67], &tfa_303196, sizeof(r_device)); + memcpy(&cfg->devices[68], &tfa_marbella, sizeof(r_device)); + memcpy(&cfg->devices[69], &tpms_abarth124, sizeof(r_device)); + memcpy(&cfg->devices[70], &tpms_ave, sizeof(r_device)); + memcpy(&cfg->devices[71], &tpms_citroen, sizeof(r_device)); + memcpy(&cfg->devices[72], &tpms_elantra2012, sizeof(r_device)); + memcpy(&cfg->devices[73], &tpms_ford, sizeof(r_device)); + memcpy(&cfg->devices[74], &tpms_hyundai_vdo, sizeof(r_device)); + memcpy(&cfg->devices[75], &tpms_jansite, sizeof(r_device)); + memcpy(&cfg->devices[76], &tpms_jansite_solar, sizeof(r_device)); + memcpy(&cfg->devices[77], &tpms_kia, sizeof(r_device)); + memcpy(&cfg->devices[78], &tpms_nissan, sizeof(r_device)); + memcpy(&cfg->devices[79], &tpms_pmv107j, sizeof(r_device)); + memcpy(&cfg->devices[80], &tpms_porsche, sizeof(r_device)); + memcpy(&cfg->devices[81], &tpms_renault, sizeof(r_device)); + memcpy(&cfg->devices[82], &tpms_renault_0435r, sizeof(r_device)); + memcpy(&cfg->devices[83], &tpms_toyota, sizeof(r_device)); + memcpy(&cfg->devices[84], &tpms_truck, sizeof(r_device)); } - // end of fragement + // end of fragment diff --git a/tools/rtl_433_devices.fragment b/tools/rtl_433_devices.fragment index 9048c845..55e793c0 100644 --- a/tools/rtl_433_devices.fragment +++ b/tools/rtl_433_devices.fragment @@ -28,6 +28,8 @@ DECL(bresser_5in1) \ DECL(bresser_6in1) \ DECL(bresser_7in1) \ + DECL(bresser_leakage) \ + DECL(bresser_lightning) \ DECL(bt_rain) \ DECL(burnhardbbq) \ DECL(calibeur_RF104) \ @@ -71,12 +73,14 @@ DECL(fineoffset_WH51) \ DECL(fineoffset_WH0530) \ DECL(fineoffset_wh1050) \ + DECL(tfa_303151) \ DECL(fineoffset_wh1080) \ DECL(fineoffset_wh1080_fsk) \ DECL(fineoffset_wh31l) \ DECL(fineoffset_wh45) \ DECL(fineoffset_wn34) \ DECL(fineoffset_ws80) \ + DECL(fineoffset_ws90) \ DECL(flowis) \ DECL(fordremote) \ DECL(fs20) \ @@ -175,6 +179,7 @@ DECL(rubicson_48659) \ DECL(rubicson_pool_48942) \ DECL(s3318p) \ + DECL(schou_72543_rain) \ DECL(schraeder) \ DECL(schrader_EG53MA4) \ DECL(schrader_SMD3MA4) \ @@ -203,6 +208,7 @@ DECL(thermopro_tp11) \ DECL(thermopro_tp12) \ DECL(thermopro_tx2) \ + DECL(thermopro_tx2c) \ DECL(tpms_abarth124) \ DECL(tpms_ave) \ DECL(tpms_citroen) \ @@ -213,6 +219,7 @@ DECL(tpms_jansite) \ DECL(tpms_jansite_solar) \ DECL(tpms_kia) \ + DECL(tpms_nissan) \ DECL(tpms_pmv107j) \ DECL(tpms_porsche) \ DECL(tpms_renault) \ @@ -236,5 +243,5 @@ DECL(x10_sec) \ DECL(yale_hsa) \ /* Add new decoders here. */ -#define NUMOF_OOK_DEVICES 157 -#define NUMOF_FSK_DEVICES 80 +#define NUMOF_OOK_DEVICES 159 +#define NUMOF_FSK_DEVICES 85