├── .gitmodules ├── .gitignore ├── example ├── .gitignore ├── FSK_Transmitter │ ├── .gitignore │ ├── platformio.ini │ └── README.md └── OOK_Receiver │ ├── .gitignore │ ├── OOK_Receiver.ino │ └── platformio.ini ├── docs └── E07-M1101D-TH_Usermanual_EN_v1.30.pdf ├── tools ├── rtl_433_devices.pre ├── rtl_433_devices.post ├── signal_parser ├── device.awk ├── rtl_433_devices.fragment └── update_rtl_433_devices.sh ├── include ├── rtl_devices.h ├── decoder.h ├── compat_time.h ├── samp_grab.h ├── abuf.h ├── fatal.h ├── am_analyze.h ├── list.h ├── log.h ├── r_private.h ├── r_device.h ├── r_api.h ├── term_ctl.h └── decoder_util.h ├── library.json ├── src ├── receiver.cpp ├── receiver.h ├── tools │ ├── aprintf.h │ └── aprintf.cpp ├── rtl_433 │ ├── abuf.c │ ├── devices │ │ ├── silvercrest.c │ │ ├── elro_db286a.c │ │ ├── quhwa.c │ │ ├── akhan_100F14.c │ │ ├── blyss.c │ │ ├── generic_temperature_sensor.c │ │ ├── ft004b.c │ │ ├── rftech.c │ │ ├── thermopro_tp11.c │ │ ├── generic_motion.c │ │ ├── auriol_4ld5661.c │ │ ├── generic_remote.c │ │ ├── intertechno.c │ │ ├── mebus.c │ │ ├── nice_flor_s.c │ │ ├── eurochron.c │ │ ├── inovalley-kw9015b.c │ │ ├── tfa_pool_thermometer.c │ │ ├── waveman.c │ │ ├── wt0124.c │ │ ├── solight_te44.c │ │ ├── chuango.c │ │ ├── wg_pb12v1.c │ │ ├── opus_xt300.c │ │ ├── ht680.c │ │ ├── rubicson.c │ │ ├── auriol_ahfl.c │ │ ├── auriol_hg02832.c │ │ ├── tfa_30_3221.c │ │ ├── efth800.c │ │ ├── wssensor.c │ │ ├── bt_rain.c │ │ ├── prologue.c │ │ ├── dish_remote_6_3.c │ │ ├── kedsum.c │ │ ├── kerui.c │ │ ├── maverick_et73.c │ │ ├── oregon_scientific_v1.c │ │ ├── bresser_3ch.c │ │ ├── cardin.c │ │ ├── fs20.c │ │ ├── ts_ft002.c │ │ └── hcs200.c │ └── list.c └── decoder.h └── signals └── acurite_986.md /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rtl_433"] 2 | path = rtl_433 3 | url = https://github.com/merbanan/rtl_433 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode 3 | /rtl_433 4 | 5 | tools/copy.list 6 | tools/devices.list 7 | tools/rtl_433_ESP.fragment 8 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /docs/E07-M1101D-TH_Usermanual_EN_v1.30.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g3gg0/rtl_433_ESP/master/docs/E07-M1101D-TH_Usermanual_EN_v1.30.pdf -------------------------------------------------------------------------------- /example/FSK_Transmitter/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /example/OOK_Receiver/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /tools/rtl_433_devices.pre: -------------------------------------------------------------------------------- 1 | /** @file 2 | This is a generated file from tools/update_rtl_433_devices.sh 3 | */ 4 | 5 | #ifndef INCLUDE_RTL_433_DEVICES_H_ 6 | #define INCLUDE_RTL_433_DEVICES_H_ 7 | 8 | #include "r_device.h" 9 | 10 | #ifndef MY_DEVICES 11 | #define DEVICES \ 12 | -------------------------------------------------------------------------------- /include/rtl_devices.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RTL_DEVICES_H_ 2 | #define INCLUDE_RTL_DEVICES_H_ 3 | 4 | 5 | int pulse_demod_ppm(const pulse_data_t *pulses, r_device *device); 6 | 7 | int prologue_callback(r_device *decoder, bitbuffer_t *bitbuffer); 8 | extern r_device prologue; 9 | //void prologue_callback(); 10 | 11 | 12 | #endif /* INCLUDE_RTL_DEVICES_H_ */ -------------------------------------------------------------------------------- /include/decoder.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | Meta include for all decoders. 3 | */ 4 | 5 | #ifndef INCLUDE_DECODER_H_ 6 | #define INCLUDE_DECODER_H_ 7 | 8 | #include 9 | #include 10 | #include "r_device.h" 11 | #include "bitbuffer.h" 12 | #include "data.h" 13 | #include "util.h" 14 | #include "decoder_util.h" 15 | 16 | #endif /* INCLUDE_DECODER_H_ */ 17 | -------------------------------------------------------------------------------- /tools/rtl_433_devices.post: -------------------------------------------------------------------------------- 1 | /* Add new decoders here. */ 2 | #else 3 | /** 4 | * Subset of devices that I have access to and have tested with 5 | */ 6 | #define DEVICES \ 7 | DECL(acurite_986) \ 8 | DECL(skylink_motion) \ 9 | DECL(prologue) \ 10 | DECL(philips_aj3650) \ 11 | DECL(fineoffset_WH51) \ 12 | /* Add new personal decoders here. */ 13 | #define NUMOFDEVICES 5 14 | #endif 15 | 16 | #define DECL(name) extern r_device name; 17 | DEVICES 18 | #undef DECL 19 | 20 | #ifdef RTL_FLEX 21 | extern r_device *flex_create_device(char *spec); // maybe put this in some header file? 22 | #define NUMOFDEVICES 102 23 | #endif 24 | 25 | #endif /* INCLUDE_RTL_433_DEVICES_H_ */ 26 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rtl_433_ESP", 3 | "keywords": "rtl_433, 433MHz, 434MHz, rf", 4 | "description": "RTL_433 433.92 MHz protocols library for Arduino. With this port of RTL_433 433.92 MHz protocols, you can receive and parse all 434 MHz protocols (e.g., rc switches or weather stations) supported by rtl_433. This should help to implement IoT bridges between the 434MHz-RF band and internet protocols. It is developed and tested on ESP32.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/NorthernMan54/rtl_433_ESP.git" 8 | }, 9 | "version": "0.1.3", 10 | "license": "GPL-3.0", 11 | "frameworks": "arduino", 12 | "platforms": [ 13 | "espressif32" 14 | ], 15 | "dependencies": { 16 | "jgromes/RadioLib": "^5.3.0" 17 | } 18 | } -------------------------------------------------------------------------------- /include/compat_time.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | compat_time addresses compatibility time functions. 3 | 4 | topic: high-resolution timestamps 5 | issue: is not available on Windows systems 6 | solution: provide a compatible version for Windows systems 7 | */ 8 | 9 | #ifndef INCLUDE_COMPAT_TIME_H_ 10 | #define INCLUDE_COMPAT_TIME_H_ 11 | 12 | // ensure struct timeval is known 13 | #ifdef _WIN32 14 | #include 15 | #else 16 | #include 17 | #endif 18 | 19 | /** Subtract `struct timeval` values. 20 | 21 | @param[out] result time difference result 22 | @param x first time value 23 | @param y second time value 24 | @return 1 if the difference is negative, otherwise 0. 25 | */ 26 | int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y); 27 | 28 | // platform-specific functions 29 | 30 | #ifdef _WIN32 31 | int gettimeofday(struct timeval *tv, void *tz); 32 | #endif 33 | 34 | #endif /* INCLUDE_COMPAT_TIME_H_ */ 35 | -------------------------------------------------------------------------------- /tools/signal_parser: -------------------------------------------------------------------------------- 1 | # .name = "LaCrosse TX141-Bv2, TX141TH-Bv2, TX141-Bv3, TX141W, TX145wsdth sensor", 2 | # .modulation = OOK_PULSE_PWM, 3 | # .short_width = 208, // short pulse is 208 us + 417 us gap 4 | # .long_width = 417, // long pulse is 417 us + 208 us gap 5 | # .sync_width = 833, // sync pulse is 833 us + 833 us gap 6 | # .gap_limit = 625, // long gap (with short pulse) is ~417 us, sync gap is ~833 us 7 | # .reset_limit = 1700, // maximum gap is 1250 us (long gap + longer sync gap on last repeat) 8 | 9 | 10 | # MID = (long_width - short_width) / 2 + short_width 11 | 12 | export MIN=1500 13 | export MID=3000 14 | export GAP=20000 15 | export RESET=1000000 16 | 17 | sed -e 's/+/,/g' | sed -e 's/-/,-/g' | tr ',' '\n' | \ 18 | awk -v MIN=$MIN -v MID=$MID -v GAP=$GAP -v RESET=$RESET '{ if ( $1 < -1000000) print "E"; if ( $1 > MIN && $1 <= MID ) print "0"; if ( $1 > MID && $1 <= GAP ) print "1"; if ( $1 > GAP && $1 <= RESET ) print "R"; if ( $1 > RESET ) print "L"; }' | \ 19 | tr -d '\n' | tr -d '\n' | tr 'R' '\n' | tr 'E' '\n' | sed '/^$/d' 20 | -------------------------------------------------------------------------------- /src/receiver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | rtl_433_ESP - 433.92 MHz protocols library for ESP32 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 3 of the License, or (at your option) any later version. 8 | This library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Lesser General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with library. If not, see 15 | 16 | 17 | Project Structure 18 | 19 | rtl_433_ESP - Main Class 20 | decoder.cpp - Wrapper and interface for the rtl_433 classes 21 | receiver.cpp - Wrapper and interface for RadioLib 22 | rtl_433 - subset of rtl_433 package 23 | 24 | */ 25 | 26 | #include "receiver.h" 27 | 28 | int totalRssi = 0; 29 | int rssiCount = 0; 30 | 31 | void _loop() 32 | { 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/receiver.h: -------------------------------------------------------------------------------- 1 | /* 2 | rtl_433_ESP - 433.92 MHz protocols library for ESP32 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 3 of the License, or (at your option) any later version. 8 | This library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Lesser General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with library. If not, see 15 | 16 | 17 | Project Structure 18 | 19 | rtl_433_ESP - Main Class 20 | decoder.cpp - Wrapper and interface for the rtl_433 classes 21 | receiver.cpp - Wrapper and interface for RadioLib 22 | rtl_433 - subset of rtl_433 package 23 | 24 | */ 25 | 26 | 27 | #ifndef rtl_433_RECEIVER_H 28 | #define rtl_433_RECEIVER_H 29 | 30 | #include "tools/aprintf.h" 31 | #include "log.h" 32 | 33 | #endif -------------------------------------------------------------------------------- /include/samp_grab.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | IQ sample grabber (ring buffer and dumper). 3 | 4 | Copyright (C) 2018 Christian Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #ifndef INCLUDE_SAMP_GRAB_H_ 13 | #define INCLUDE_SAMP_GRAB_H_ 14 | 15 | #include 16 | 17 | typedef struct samp_grab { 18 | uint32_t *frequency; 19 | uint32_t *samp_rate; 20 | int *sample_size; 21 | 22 | unsigned sg_counter; 23 | char *sg_buf; 24 | unsigned sg_size; 25 | unsigned sg_index; 26 | unsigned sg_len; 27 | } samp_grab_t; 28 | 29 | samp_grab_t *samp_grab_create(unsigned size); 30 | 31 | void samp_grab_free(samp_grab_t *g); 32 | 33 | void samp_grab_push(samp_grab_t *g, unsigned char *iq_buf, uint32_t len); 34 | 35 | void samp_grab_reset(samp_grab_t *g); 36 | 37 | /// grab_end is counted in samples from end of buf. 38 | void samp_grab_write(samp_grab_t *g, unsigned grab_len, unsigned grab_end); 39 | 40 | #endif /* INCLUDE_SAMP_GRAB_H_ */ 41 | -------------------------------------------------------------------------------- /include/abuf.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | array buffer (string builder). 3 | 4 | Copyright (C) 2018 Christian Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #ifndef INCLUDE_ABUF_H_ 13 | #define INCLUDE_ABUF_H_ 14 | 15 | #if defined _MSC_VER || defined ESP32 // Microsoft Visual Studio or ESP32 16 | // MSC and ESP32 have something like C99 restrict as __restrict 17 | #ifndef restrict 18 | #define restrict __restrict 19 | #endif 20 | #endif 21 | // Defined in newer for MSVC. 22 | #ifndef _Printf_format_string_ 23 | #define _Printf_format_string_ 24 | #endif 25 | 26 | #include 27 | 28 | typedef struct abuf { 29 | char *head; 30 | char *tail; 31 | size_t left; 32 | } abuf_t; 33 | 34 | void abuf_init(abuf_t *buf, char *dst, size_t len); 35 | 36 | void abuf_setnull(abuf_t *buf); 37 | 38 | char *abuf_push(abuf_t *buf); 39 | 40 | void abuf_pop(abuf_t *buf, char *end); 41 | 42 | void abuf_cat(abuf_t *buf, const char *str); 43 | 44 | int abuf_printf(abuf_t *buf, _Printf_format_string_ char const *restrict format, ...) 45 | #if defined(__GNUC__) || defined(__clang__) 46 | __attribute__((format(printf, 2, 3))) 47 | #endif 48 | ; 49 | 50 | #endif /* INCLUDE_ABUF_H_ */ 51 | -------------------------------------------------------------------------------- /src/tools/aprintf.h: -------------------------------------------------------------------------------- 1 | /* 2 | ESPiLight - pilight 433.92 MHz protocols library for Arduino 3 | Copyright (c) 2016 Puuu. All right reserved. 4 | 5 | Project home: https://github.com/puuu/espilight/ 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 3 of the License, or (at your option) any later version. 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with library. If not, see 17 | */ 18 | 19 | #ifndef _APRINTF_H_ 20 | #define _APRINTF_H_ 21 | 22 | #include 23 | 24 | #ifndef __cplusplus 25 | #include 26 | #define fprintf(stream, fmt, ...) aprintf_P(PSTR(fmt), ##__VA_ARGS__) 27 | #define printf(fmt, ...) aprintf_P(PSTR(fmt), ##__VA_ARGS__) 28 | #endif 29 | 30 | #ifdef __cplusplus 31 | #include 32 | void set_aprintf_output(Print *output); 33 | #endif 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | void exit(int n); 39 | int aprintf_P(PGM_P formatP, ...) __attribute__((format(printf, 1, 2))); 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif //_APRINTF_H_ 45 | -------------------------------------------------------------------------------- /tools/device.awk: -------------------------------------------------------------------------------- 1 | # rtl_433 2 | # this was created 3 | # egrep "\.name|\.modulation|\.decode_fn|^r_device " *.c > device.list 4 | # List of files 5 | # cat device.list | awk -f device.awk | egrep "OOK_PULSE_PWM|OOK_PULSE_PPM" | awk -F : '{ print $1 }' | sort | uniq > list 6 | # Update to rtl_433_devices.h 7 | # cat device.list | awk -f device.awk | egrep "OOK_PULSE_PWM|OOK_PULSE_PPM" | awk -F\" '{ print $3 }' | awk -F, '{ print $3 }' | awk '{ print "DECL("$1") \\" }' 8 | 9 | # acurite.c:r_device acurite_rain_896 = { 10 | # acurite.c: .name = "Acurite 896 Rain Gauge", 11 | # acurite.c: .modulation = OOK_PULSE_PPM, 12 | # acurite.c: .decode_fn = &acurite_rain_896_decode, 13 | # acurite.c: .name = "Acurite 609TXC Temperature and Humidity Sensor", 14 | # acurite.c: .modulation = OOK_PULSE_PPM, 15 | # acurite.c: .decode_fn = &acurite_th_decode, 16 | # acurite.c: .name = "Acurite 592TXR Temp/Humidity, 5n1 Weather Station, 6045 Lightning, 3N1, Atlas", 17 | # acurite.c: .modulation = OOK_PULSE_PWM, 18 | # acurite.c: .decode_fn = &acurite_txr_decode, 19 | 20 | { 21 | if ( $4 == "{" ) { 22 | module = $2 23 | } 24 | if ( $2 == ".name" ) { 25 | for(i=4;i<=NF;i++){name=name" "$i} 26 | file = $1 27 | } 28 | if ( $2 == ".modulation" ) { 29 | modulation = $4 30 | } 31 | if ( $2 == ".decode_fn" ) { 32 | decode_fn = $4 33 | print $1",", name, modulation, module 34 | name = "" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /example/FSK_Transmitter/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = esp32 13 | src_dir = . 14 | 15 | [libraries] 16 | arduinolog = https://github.com/1technophile/Arduino-Log.git#d13cd80 17 | arduinojson = ArduinoJson@5.13.4 18 | rtl_433_ESP = https://github.com/NorthernMan54/rtl_433_ESP 19 | smartrc-cc1101-driver-lib = SmartRC-CC1101-Driver-Lib@2.5.5 20 | 21 | [env] 22 | framework = arduino 23 | platform = espressif32 24 | lib_ldf_mode = chain+ 25 | lib_deps = 26 | ; ${libraries.arduinolog} 27 | ; ${libraries.arduinojson} 28 | ; ${libraries.rtl_433_ESP} 29 | ; ${libraries.smartrc-cc1101-driver-lib} 30 | 31 | [env:esp32] 32 | monitor_speed = 921600 33 | monitor_filters = esp32_exception_decoder 34 | board = esp32dev 35 | build_type = debug 36 | build_flags = 37 | '-DRF_EMITTER_GPIO=12' 38 | '-DRF_RECEIVER_GPIO=27' 39 | '-DLOG_LEVEL=LOG_LEVEL_TRACE' 40 | '-DRTL_DEBUG=4' ; rtl_433 verbose mode 41 | ; '-DRAW_SIGNAL_DEBUG=true' ; display raw received messages 42 | ; '-DMEMORY_DEBUG=true' ; display memory usage information 43 | ; '-DMY_DEVICES=true' ; subset of devices 44 | monitor_port = /dev/cu.SLAB_USBtoUART 45 | upload_port = /dev/cu.SLAB_USBtoUART 46 | upload_speed = 921600 -------------------------------------------------------------------------------- /include/fatal.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | Fatal abort and warning macros for allocs. 3 | 4 | Copyright (C) 2019 Christian W. Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #ifndef INCLUDE_FATAL_H_ 13 | #define INCLUDE_FATAL_H_ 14 | 15 | #define STRINGIFYX(x) #x 16 | #define STRINGIFY(x) STRINGIFYX(x) 17 | #define FILE_LINE __FILE__ ":" STRINGIFY(__LINE__) 18 | #define FATAL(what) do { fprintf(stderr, "FATAL: " what " from " FILE_LINE "\n"); exit(1); } while (0) 19 | #define FATAL_MALLOC(what) FATAL("low memory? malloc() failed in " what) 20 | #define FATAL_CALLOC(what) FATAL("low memory? calloc() failed in " what) 21 | #define FATAL_REALLOC(what) FATAL("low memory? realloc() failed in " what) 22 | #define FATAL_STRDUP(what) FATAL("low memory? strdup() failed in " what) 23 | #define WARN(what) fprintf(stderr, "WARNING: " what " from " FILE_LINE "\n") 24 | #define WARN_MALLOC(what) WARN("low memory? malloc() failed in " what) 25 | #define WARN_CALLOC(what) WARN("low memory? calloc() failed in " what) 26 | #define WARN_REALLOC(what) WARN("low memory? realloc() failed in " what) 27 | #define WARN_STRDUP(what) WARN("low memory? strdup() failed in " what) 28 | 29 | /* 30 | Use like this: 31 | 32 | char *buf = malloc(size); 33 | if (!buf) 34 | FATAL_MALLOC("my_func()"); 35 | 36 | */ 37 | 38 | #endif /* INCLUDE_FATAL_H_ */ 39 | -------------------------------------------------------------------------------- /signals/acurite_986.md: -------------------------------------------------------------------------------- 1 | 2 | rtl_433_ESP(6): RAW (152693): +63-163+206-291+1601-1577+1594-1567+1599-1586+1592-1394+209-536+179-548+179-545+203-548+176-535+210-541+175-542+206-532+210-536+178-551+168-562+178-612+206-880+207-594+210-893+197-539+190-611+169-983+207-960+183-962+200-976+180-895+203-532+205-599+175-907+210-538+176-563+178-548+180-552+204-521+207-537+173-566+178-613+182-906+202-601+176-922+170-557+183-543+201-548+173-609+196-288+202-284+1593-1595+1575-1586+1612-1564+1607-1384+201-519+205-544+205-532+197-540+183-544+180-551+206-523+203-536+197-541+180-546+179-553+205-574+202-898+198-590+200-911+207-517+205-597+201-969+198-962+172-986+204-961+182-893+206-532+209-586+209-898+196-536+205-517+203-549+181-529+208-539+199-512+207-537+207-600+175-921+197-584+200-904+208-537+171-533+219-539 3 | rtl_433_ESP(7): demod(5) - Skylink HA-434TL motion sensor - 5636 4 | rtl_433_ESP(7): demod(5) - Prologue, FreeTec NC-7104, NC-7159-675 temperature sensor - 5636 5 | N: Subject: /RTL_433toMQTT 6 | N: Received json : {"model":"Acurite-986","id":20542,"channel":"2F","battery":"OK","temperature_C":-17.7778,"status":0,"mic":"CRC","protocol":"Acurite 986 Refrigerator / Freezer Thermometer","rssi":-65,"duration":152693} 7 | N: Subject: /RTL_433toMQTT 8 | N: Received json : {"model":"Acurite-986","id":20542,"channel":"2F","battery":"OK","temperature_C":-17.7778,"status":0,"mic":"CRC","protocol":"Acurite 986 Refrigerator / Freezer Thermometer","rssi":-65,"duration":152693} 9 | rtl_433_ESP(7): demod(5) - Acurite 986 Refrigerator / Freezer Thermometer - 5636 10 | rtl_433_ESP(7): demod(6) - Philips outdoor temperature sensor (type AJ3650) - 5636 -------------------------------------------------------------------------------- /include/am_analyze.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | AM signal analyzer. 3 | 4 | Copyright (C) 2018 Christian Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #ifndef INCLUDE_AM_ANALYZE_H_ 13 | #define INCLUDE_AM_ANALYZE_H_ 14 | 15 | #include 16 | #include "samp_grab.h" 17 | 18 | #define PULSE_DATA_SIZE 4000 /* maximum number of pulses */ 19 | 20 | typedef struct am_analyze { 21 | int level_limit; 22 | int override_short; 23 | int override_long; 24 | uint32_t *frequency; 25 | uint32_t *samp_rate; 26 | int *sample_size; 27 | 28 | /* state */ 29 | unsigned counter; 30 | unsigned print; 31 | unsigned print2; 32 | unsigned pulses_found; 33 | unsigned prev_pulse_start; 34 | unsigned pulse_start; 35 | unsigned pulse_end; 36 | unsigned pulse_avg; 37 | unsigned signal_start; 38 | unsigned signal_pulse_counter; 39 | unsigned signal_pulse_data[4000][3]; 40 | } am_analyze_t; 41 | 42 | /// Create an AM-Analyzer. Might fail and return NULL. 43 | am_analyze_t *am_analyze_create(void); 44 | 45 | void am_analyze_free(am_analyze_t *a); 46 | 47 | void am_analyze_skip(am_analyze_t *a, unsigned n_samples); 48 | 49 | void am_analyze(am_analyze_t *a, int16_t *am_buf, unsigned n_samples, int debug_output, samp_grab_t *g); 50 | 51 | void am_analyze_classify(am_analyze_t *aa); 52 | 53 | #endif /* INCLUDE_AM_ANALYZE_H_ */ 54 | -------------------------------------------------------------------------------- /example/OOK_Receiver/OOK_Receiver.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Basic rtl_433_ESP example for OOK/ASK Devices 3 | 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef RF_MODULE_FREQUENCY 11 | #define RF_MODULE_FREQUENCY 433.92 12 | #endif 13 | 14 | #define JSON_MSG_BUFFER 512 15 | 16 | char messageBuffer[JSON_MSG_BUFFER]; 17 | 18 | rtl_433_ESP rf(-1); // use -1 to disable transmitter 19 | 20 | void rtl_433_Callback(char* message) { 21 | DynamicJsonBuffer jsonBuffer2(JSON_MSG_BUFFER); 22 | JsonObject& RFrtl_433_ESPdata = jsonBuffer2.parseObject(message); 23 | logJson(RFrtl_433_ESPdata); 24 | } 25 | 26 | void logJson(JsonObject &jsondata) 27 | { 28 | #if defined(ESP8266) || defined(ESP32) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) 29 | char JSONmessageBuffer[jsondata.measureLength() + 1]; 30 | #else 31 | char JSONmessageBuffer[JSON_MSG_BUFFER]; 32 | #endif 33 | jsondata.printTo(JSONmessageBuffer, sizeof(JSONmessageBuffer)); 34 | Log.notice(F("Received message : %s" CR), JSONmessageBuffer); 35 | } 36 | 37 | void setup() 38 | { 39 | Serial.begin(921600); 40 | delay(1000); 41 | #ifndef LOG_LEVEL 42 | LOG_LEVEL_SILENT 43 | #endif 44 | Log.begin(LOG_LEVEL, &Serial); 45 | Log.notice(F(" " CR)); 46 | Log.notice(F("****** setup ******" CR)); 47 | rf.initReceiver(RF_MODULE_RECEIVER_GPIO, RF_MODULE_FREQUENCY); 48 | rf.setCallback(rtl_433_Callback, messageBuffer, JSON_MSG_BUFFER); 49 | // ELECHOUSE_cc1101.SetRx(CC1101_FREQUENCY); // set Receive on 50 | rf.enableReceiver(RF_MODULE_RECEIVER_GPIO); 51 | Log.notice(F("****** setup complete ******" CR)); 52 | } 53 | 54 | void loop() 55 | { 56 | rf.loop(); 57 | } -------------------------------------------------------------------------------- /src/rtl_433/abuf.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | array buffer (string builder). 3 | 4 | Copyright (C) 2018 Christian Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "abuf.h" 18 | 19 | void abuf_init(abuf_t *buf, char *dst, size_t len) 20 | { 21 | buf->head = dst; 22 | buf->tail = dst; 23 | buf->left = len; 24 | } 25 | 26 | void abuf_setnull(abuf_t *buf) 27 | { 28 | buf->head = NULL; 29 | buf->tail = NULL; 30 | buf->left = 0; 31 | } 32 | 33 | char *abuf_push(abuf_t *buf) 34 | { 35 | return buf->tail; 36 | } 37 | 38 | void abuf_pop(abuf_t *buf, char *end) 39 | { 40 | buf->left += buf->tail - end; 41 | buf->tail = end; 42 | } 43 | 44 | void abuf_cat(abuf_t *buf, char const *str) 45 | { 46 | size_t len = strlen(str); 47 | if (buf->left >= len + 1) { 48 | strcpy(buf->tail, str); 49 | buf->tail += len; 50 | buf->left -= len; 51 | } 52 | } 53 | 54 | int abuf_printf(abuf_t *buf, _Printf_format_string_ char const *restrict format, ...) 55 | { 56 | va_list ap; 57 | va_start(ap, format); 58 | 59 | int n = vsnprintf(buf->tail, buf->left, format, ap); 60 | 61 | if (n > 0) { 62 | size_t len = (size_t)n < buf->left ? (size_t)n : buf->left; 63 | buf->tail += len; 64 | buf->left -= len; 65 | } 66 | 67 | va_end(ap); 68 | return n; 69 | } 70 | -------------------------------------------------------------------------------- /src/decoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | rtl_433_ESP - 433.92 MHz protocols library for ESP32 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 3 of the License, or (at your option) any later version. 8 | This library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Lesser General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with library. If not, see 15 | 16 | 17 | Project Structure 18 | 19 | rtl_433_ESP - Main Class 20 | decoder.cpp - Wrapper and interface for the rtl_433 classes 21 | receiver.cpp - Wrapper and interface for RadioLib 22 | rtl_433 - subset of rtl_433 package 23 | 24 | */ 25 | 26 | #ifndef rtl_433_DECODER_H 27 | #define rtl_433_DECODER_H 28 | 29 | #include "rtl_433_ESP.h" 30 | 31 | extern "C" 32 | { 33 | #include "bitbuffer.h" 34 | #include "pulse_detect.h" 35 | #include "pulse_demod.h" 36 | #include "list.h" 37 | #include "r_api.h" 38 | #include "r_private.h" 39 | #include "rtl_433.h" 40 | #include "rtl_433_devices.h" 41 | #include "fatal.h" 42 | } 43 | 44 | #include "tools/aprintf.h" 45 | #include "log.h" 46 | 47 | void rtlSetup(); 48 | void _setCallback(rtl_433_ESPCallBack callback, char *messageBuffer, int bufferSize); 49 | void _setDebug(int debug); 50 | void processSignal(pulse_data_t *rtl_pulses); 51 | void rtl_433_DecoderTask(void *pvParameters); 52 | 53 | #endif -------------------------------------------------------------------------------- /include/list.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | Generic list. 3 | 4 | Copyright (C) 2018 Christian Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #ifndef INCLUDE_LIST_H_ 13 | #define INCLUDE_LIST_H_ 14 | 15 | #include 16 | 17 | /// Dynamically growing list, elems is always NULL terminated, call list_ensure_size() to alloc elems. 18 | typedef struct list { 19 | void **elems; 20 | size_t size; 21 | size_t len; 22 | } list_t; 23 | 24 | typedef void (*list_elem_free_fn)(void *); 25 | 26 | /// Alloc elems if needed and ensure the list has room for at least min_size elements. 27 | void list_ensure_size(list_t *list, size_t min_size); 28 | 29 | /// Add to the end of elems, allocs or grows the list if needed and ensures the list has a terminating NULL. 30 | void list_push(list_t *list, void *p); 31 | 32 | /// Adds all elements of a NULL terminated list to the end of elems, allocs or grows the list if needed and ensures the list has a terminating NULL. 33 | void list_push_all(list_t *list, void **p); 34 | 35 | /// Remove element from the list, frees element with fn. 36 | void list_remove(list_t *list, size_t idx, list_elem_free_fn elem_free); 37 | 38 | /// Clear the list, frees each element with fn, does not free backing or list itself. 39 | void list_clear(list_t *list, list_elem_free_fn elem_free); 40 | 41 | /// Clear the list, free backing, does not free list itself. 42 | void list_free_elems(list_t *list, list_elem_free_fn elem_free); 43 | 44 | #endif /* INCLUDE_LIST_H_ */ 45 | -------------------------------------------------------------------------------- /include/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | rtl_433_ESP - 433.92 MHz protocols library for ESP32 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 3 of the License, or (at your option) any later version. 8 | This library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Lesser General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with library. If not, see 15 | 16 | 17 | Project Structure 18 | 19 | rtl_433_ESP - Main Class 20 | decoder.cpp - Wrapper and interface for the rtl_433 classes 21 | receiver.cpp - Wrapper and interface for RadioLib 22 | rtl_433 - subset of rtl_433 package 23 | 24 | */ 25 | 26 | #ifndef _LOG_H_ 27 | #define _LOG_H_ 28 | 29 | #define LOG_EMERG 0 30 | #define LOG_ALERT 1 31 | #define LOG_CRIT 2 32 | #define LOG_ERR 3 33 | #define LOG_WARNING 4 34 | #define LOG_NOTICE 5 35 | #define LOG_INFO 6 36 | #define LOG_DEBUG 7 37 | #define LOG_TRACE 8 38 | #define LOG_VERBOSE 9 39 | 40 | 41 | #define LOG_STACK 255 42 | 43 | #include 44 | 45 | #define logprintf(prio, args...) {printf("rtl_433_ESP(%d): ", prio);printf(args);} 46 | #define logprintfLn(prio, args...) {printf("rtl_433_ESP(%d): ", prio);printf(args);printf("\n");} 47 | #define alogprintf(prio, args...) {printf(args);} 48 | #define alogprintfLn(prio, args...) {printf(args);printf("\n");} 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /include/r_private.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | Definition of r_private state structure. 3 | */ 4 | 5 | #ifndef INCLUDE_R_PRIVATE_H_ 6 | #define INCLUDE_R_PRIVATE_H_ 7 | 8 | #include 9 | #include 10 | #include "list.h" 11 | #include "baseband.h" 12 | #include "pulse_detect.h" 13 | #include "fileformat.h" 14 | #include "samp_grab.h" 15 | #include "am_analyze.h" 16 | #include "rtl_433.h" 17 | #include "compat_time.h" 18 | 19 | struct dm_state { 20 | // float level_limit; 21 | // float min_level; 22 | // float min_snr; 23 | // int use_mag_est; 24 | // int detect_verbosity; 25 | 26 | // int16_t am_buf[MAXIMAL_BUF_LENGTH]; // AM demodulated signal (for OOK decoding) 27 | // union { 28 | // These buffers aren't used at the same time, so let's use a union to save some memory 29 | // int16_t fm[MAXIMAL_BUF_LENGTH]; // FM demodulated signal (for FSK decoding) 30 | // uint16_t temp[MAXIMAL_BUF_LENGTH]; // Temporary buffer (to be optimized out..) 31 | // } buf; 32 | // uint8_t u8_buf[MAXIMAL_BUF_LENGTH]; // format conversion buffer 33 | // float f32_buf[MAXIMAL_BUF_LENGTH]; // format conversion buffer 34 | // int sample_size; // CU8: 1, CS16: 2 35 | // pulse_detect_t *pulse_detect; 36 | // filter_state_t lowpass_filter_state; 37 | // demodfm_state_t demod_FM_state; 38 | // int enable_FM_demod; 39 | // unsigned fsk_pulse_detect_mode; 40 | // unsigned frequency; 41 | // samp_grab_t *samp_grab; 42 | // am_analyze_t *am_analyze; 43 | // int analyze_pulses; 44 | // file_info_t load_info; 45 | // list_t dumper; 46 | 47 | /* Protocol states */ 48 | list_t r_devs; 49 | 50 | pulse_data_t *pulse_data; 51 | // pulse_data_t fsk_pulse_data; 52 | // unsigned frame_event_count; 53 | // unsigned frame_start_ago; 54 | // unsigned frame_end_ago; 55 | // struct timeval now; 56 | // float sample_file_pos; 57 | }; 58 | 59 | #endif /* INCLUDE_R_PRIVATE_H_ */ 60 | -------------------------------------------------------------------------------- /src/tools/aprintf.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ESPiLight - pilight 433.92 MHz protocols library for Arduino 3 | Copyright (c) 2016 Puuu. All right reserved. 4 | 5 | Project home: https://github.com/puuu/espilight/ 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 3 of the License, or (at your option) any later version. 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with library. If not, see 17 | */ 18 | 19 | #include "aprintf.h" 20 | #include 21 | 22 | static Print *aprintf_print = nullptr; 23 | 24 | void set_aprintf_output(Print *output) { aprintf_print = output; } 25 | 26 | int aprintf_P(PGM_P formatP, ...) { 27 | if (aprintf_print == nullptr) { 28 | return 0; 29 | } 30 | va_list arg; 31 | va_start(arg, formatP); 32 | char temp[64]; 33 | char *buffer = temp; 34 | size_t len = vsnprintf_P(temp, sizeof(temp), formatP, arg); 35 | va_end(arg); 36 | if (len > sizeof(temp) - 1) { 37 | buffer = new char[len + 1]; 38 | if (!buffer) { 39 | return 0; 40 | } 41 | va_start(arg, formatP); 42 | vsnprintf_P(buffer, len + 1, formatP, arg); 43 | va_end(arg); 44 | } 45 | len = aprintf_print->write((const uint8_t *)buffer, len); 46 | if (buffer != temp) { 47 | delete[] buffer; 48 | } 49 | return len; 50 | } 51 | 52 | void exit(int n) { 53 | if (aprintf_print != nullptr) { 54 | aprintf_print->print(F("EXIT: ")); 55 | aprintf_print->println(n); 56 | } 57 | ESP.restart(); 58 | while (true) 59 | ; 60 | } 61 | -------------------------------------------------------------------------------- /src/rtl_433/devices/silvercrest.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Silvercrest remote decoder. 3 | 4 | Copyright (C) 2018 Benjamin Larsson 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #include "decoder.h" 13 | 14 | 15 | static int silvercrest_callback(r_device *decoder, bitbuffer_t *bitbuffer) 16 | { 17 | uint8_t const cmd_lu_tab[16] = {2,3,0,1,4,5,7,6,0xC,0xD,0xF,0xE,8,9,0xB,0xA}; 18 | 19 | uint8_t *b; // bits of a row 20 | uint8_t cmd; 21 | data_t *data; 22 | 23 | if (bitbuffer->bits_per_row[1] !=33) 24 | return DECODE_ABORT_LENGTH; 25 | 26 | /* select second row, first might be bad */ 27 | b = bitbuffer->bb[1]; 28 | if ((b[0] == 0x7c) && (b[1] == 0x26)) { 29 | cmd = b[2] & 0xF; 30 | // Validate button 31 | if ((b[3]&0xF) != cmd_lu_tab[cmd]) 32 | return DECODE_ABORT_EARLY; 33 | 34 | /* clang-format off */ 35 | data = data_make( 36 | "model", "", DATA_STRING, "Silvercrest-Remote", 37 | "button", "", DATA_INT, cmd, 38 | NULL); 39 | /* clang-format on */ 40 | 41 | decoder_output_data(decoder, data); 42 | 43 | return 1; 44 | } 45 | return DECODE_ABORT_EARLY; 46 | } 47 | 48 | static char *output_fields[] = { 49 | "model", 50 | "button", 51 | NULL, 52 | }; 53 | 54 | r_device silvercrest = { 55 | .name = "Silvercrest Remote Control", 56 | .modulation = OOK_PULSE_PWM, 57 | .short_width = 264, 58 | .long_width = 744, 59 | .reset_limit = 12000, 60 | .gap_limit = 5000, 61 | .decode_fn = &silvercrest_callback, 62 | .disabled = 0, 63 | .fields = output_fields, 64 | }; 65 | -------------------------------------------------------------------------------- /src/rtl_433/devices/elro_db286a.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Generic doorbell implementation for Elro DB286A devices. 3 | 4 | Copyright (C) 2016 Fabian Zaremba 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | */ 12 | /** 13 | Generic doorbell implementation for Elro DB286A devices. 14 | 15 | Note that each device seems to have two codes, which alternate 16 | for every other button press. 17 | 18 | short is 456 us pulse, 1540 us gap 19 | long is 1448 us pulse, 544 us gap 20 | packet gap is 7016 us 21 | 22 | Example code: 37f62a6c80 23 | */ 24 | 25 | 26 | #include "decoder.h" 27 | 28 | static int elro_db286a_callback(r_device *decoder, bitbuffer_t *bitbuffer) 29 | { 30 | data_t *data; 31 | uint8_t *b; 32 | char id_str[4*2+1]; 33 | 34 | // 33 bits expected, 5 minimum packet repetitions (14 expected) 35 | int row = bitbuffer_find_repeated_row(bitbuffer, 5, 33); 36 | 37 | if (row < 0 || bitbuffer->bits_per_row[row] != 33) 38 | return DECODE_ABORT_LENGTH; 39 | 40 | b = bitbuffer->bb[row]; 41 | 42 | // 32 bits, trailing bit is dropped 43 | sprintf(id_str, "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]); 44 | 45 | data = data_make( 46 | "model", "", DATA_STRING, "Elro-DB286A", 47 | "id", "ID", DATA_STRING, id_str, 48 | NULL); 49 | 50 | decoder_output_data(decoder, data); 51 | 52 | return 1; 53 | 54 | } 55 | 56 | static char *output_fields[] = { 57 | "model", 58 | "id", 59 | NULL 60 | }; 61 | 62 | r_device elro_db286a = { 63 | .name = "Elro DB286A Doorbell", 64 | .modulation = OOK_PULSE_PWM, 65 | .short_width = 456, 66 | .long_width = 1448, 67 | .gap_limit = 2000, 68 | .reset_limit = 8000, 69 | .decode_fn = &elro_db286a_callback, 70 | .disabled = 1, 71 | .fields = output_fields 72 | }; 73 | -------------------------------------------------------------------------------- /src/rtl_433/devices/quhwa.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Quhwa HS1527. 3 | 4 | Copyright (C) 2016 Ask Jakobsen 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Quhwa HS1527. 13 | 14 | Tested devices: 15 | QH-C-CE-3V (which should be compatible with QH-832AC), 16 | also sold as "1 by One" wireless doorbell 17 | */ 18 | 19 | #include "decoder.h" 20 | 21 | static int quhwa_callback(r_device *decoder, bitbuffer_t *bitbuffer) 22 | { 23 | int r = bitbuffer_find_repeated_row(bitbuffer, 5, 18); 24 | if (r < 0) 25 | return DECODE_ABORT_EARLY; 26 | 27 | uint8_t *b = bitbuffer->bb[r]; 28 | 29 | // No need to decode/extract values for simple test 30 | if (!b[0] && !b[1] && !b[2]) { 31 | if (decoder->verbose > 1) { 32 | fprintf(stderr, "%s: DECODE_FAIL_SANITY data all 0x00\n", __func__); 33 | } 34 | return DECODE_FAIL_SANITY; 35 | } 36 | 37 | b[0] = ~b[0]; 38 | b[1] = ~b[1]; 39 | b[2] = ~b[2]; 40 | 41 | if (bitbuffer->bits_per_row[r] != 18 42 | || (b[1] & 0x03) != 0x03 43 | || (b[2] & 0xC0) != 0xC0) 44 | return DECODE_ABORT_LENGTH; 45 | 46 | uint32_t id = (b[0] << 8) | b[1]; 47 | 48 | /* clang-format off */ 49 | data_t *data = data_make( 50 | "model", "", DATA_STRING, "Quhwa-Doorbell", 51 | "id", "ID", DATA_INT, id, 52 | NULL); 53 | /* clang-format on */ 54 | 55 | decoder_output_data(decoder, data); 56 | 57 | return 1; 58 | } 59 | 60 | static char *output_fields[] = { 61 | "model", 62 | "id", 63 | NULL, 64 | }; 65 | 66 | r_device quhwa = { 67 | .name = "Quhwa", 68 | .modulation = OOK_PULSE_PWM, 69 | .short_width = 360, // Pulse: Short 360µs, Long 1070µs 70 | .long_width = 1070, // Gaps: Short 360µs, Long 1070µs 71 | .reset_limit = 6600, // Intermessage Gap 6500µs 72 | .gap_limit = 1200, // Long Gap 1120µs 73 | .sync_width = 0, // No sync bit used 74 | .tolerance = 80, // us 75 | .decode_fn = &quhwa_callback, 76 | .disabled = 0, 77 | .fields = output_fields, 78 | }; 79 | -------------------------------------------------------------------------------- /src/rtl_433/list.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Generic list. 3 | 4 | Copyright (C) 2018 Christian Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #include "list.h" 13 | #include "fatal.h" 14 | #include 15 | #include 16 | 17 | void list_ensure_size(list_t *list, size_t min_size) 18 | { 19 | if (!list->elems || list->size < min_size) { 20 | list->elems = realloc(list->elems, min_size * sizeof(*list->elems)); 21 | if (!list->elems) { 22 | FATAL_REALLOC("list_ensure_size()"); 23 | } 24 | list->size = min_size; 25 | 26 | list->elems[list->len] = NULL; // ensure a terminating NULL 27 | } 28 | } 29 | 30 | void list_push(list_t *list, void *p) 31 | { 32 | if (list->len + 1 >= list->size) // account for terminating NULL 33 | list_ensure_size(list, list->size < 8 ? 8 : list->size + list->size / 2); 34 | 35 | list->elems[list->len++] = p; 36 | 37 | list->elems[list->len] = NULL; // ensure a terminating NULL 38 | } 39 | 40 | void list_push_all(list_t *list, void **p) 41 | { 42 | for (void **iter = p; iter && *iter; ++iter) 43 | list_push(list, *iter); 44 | } 45 | 46 | void list_remove(list_t *list, size_t idx, list_elem_free_fn elem_free) 47 | { 48 | if (idx >= list->len) { 49 | return; // report error? 50 | } 51 | if (elem_free) { 52 | elem_free(list->elems[idx]); 53 | } 54 | for (size_t i = idx; i < list->len; ++i) { // list might contain NULLs 55 | list->elems[i] = list->elems[i + 1]; // ensures a terminating NULL 56 | } 57 | list->len--; 58 | } 59 | 60 | void list_clear(list_t *list, list_elem_free_fn elem_free) 61 | { 62 | if (elem_free) { 63 | for (size_t i = 0; i < list->len; ++i) { // list might contain NULLs 64 | elem_free(list->elems[i]); 65 | } 66 | } 67 | list->len = 0; 68 | if (list->elems) { 69 | list->elems[0] = NULL; // ensure a terminating NULL 70 | } 71 | } 72 | 73 | void list_free_elems(list_t *list, list_elem_free_fn elem_free) 74 | { 75 | list_clear(list, elem_free); 76 | free(list->elems); 77 | list->elems = NULL; 78 | list->size = 0; 79 | } 80 | -------------------------------------------------------------------------------- /src/rtl_433/devices/akhan_100F14.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Akhan remote keyless entry system. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | */ 9 | 10 | /** 11 | Akhan remote keyless entry system. 12 | 13 | This RKE system uses a HS1527 OTP encoder (http://sc-tech.cn/en/hs1527.pdf) 14 | Each message consists of a preamble, 20 bit id and 4 data bits. 15 | 16 | (code based on chuango.c and generic_remote.c) 17 | 18 | Note: simple 24 bit fixed ID protocol (x1527 style) and should be handled by the flex decoder. 19 | */ 20 | 21 | #include "decoder.h" 22 | 23 | static int akhan_rke_callback(r_device *decoder, bitbuffer_t *bitbuffer) 24 | { 25 | data_t *data; 26 | uint8_t *b; 27 | int id; 28 | int cmd; 29 | char *cmd_str; 30 | 31 | if (bitbuffer->bits_per_row[0] != 25) 32 | return DECODE_ABORT_LENGTH; 33 | b = bitbuffer->bb[0]; 34 | 35 | //invert bits, short pulse is 0, long pulse is 1 36 | b[0] = ~b[0]; 37 | b[1] = ~b[1]; 38 | b[2] = ~b[2]; 39 | 40 | id = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); 41 | cmd = b[2] & 0x0F; 42 | switch (cmd) { 43 | case 0x1: cmd_str = "0x1 (Lock)"; break; 44 | case 0x2: cmd_str = "0x2 (Unlock)"; break; 45 | case 0x4: cmd_str = "0x4 (Mute)"; break; 46 | case 0x8: cmd_str = "0x8 (Alarm)"; break; 47 | default: cmd_str = NULL; break; 48 | } 49 | 50 | if (!cmd_str) 51 | return DECODE_FAIL_SANITY; 52 | 53 | data = data_make( 54 | "model", "", DATA_STRING, "Akhan-100F14", 55 | "id", "ID (20bit)", DATA_FORMAT, "0x%x", DATA_INT, id, 56 | "data", "Data (4bit)", DATA_STRING, cmd_str, 57 | NULL); 58 | 59 | decoder_output_data(decoder, data); 60 | return 1; 61 | } 62 | 63 | static char *output_fields[] = { 64 | "model", 65 | "id", 66 | "data", 67 | NULL 68 | }; 69 | 70 | r_device akhan_100F14 = { 71 | .name = "Akhan 100F14 remote keyless entry", 72 | .modulation = OOK_PULSE_PWM, 73 | .short_width = 316, 74 | .long_width = 1020, 75 | .reset_limit = 1800, 76 | .sync_width = 0, 77 | .tolerance = 80, // us 78 | .decode_fn = &akhan_rke_callback, 79 | .disabled = 0, 80 | .fields = output_fields, 81 | }; 82 | -------------------------------------------------------------------------------- /src/rtl_433/devices/blyss.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Generic remote Blyss DC5-UK-WH as sold by B&Q. 3 | 4 | Copyright (C) 2016 John Jore 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Generic remote Blyss DC5-UK-WH as sold by B&Q. 13 | 14 | DC5-UK-WH pair with receivers, the codes used may be specific to a receiver - use with caution 15 | 16 | warmup pulse 5552 us, 2072 gap 17 | short is 512 us pulse, 1484 us gap 18 | long is 1508 us pulse, 488 us gap 19 | packet gap is 6964 us 20 | 21 | */ 22 | #include "decoder.h" 23 | 24 | static int blyss_callback(r_device *decoder, bitbuffer_t *bitbuffer) 25 | { 26 | data_t *data; 27 | uint8_t *b; 28 | char id_str[16]; 29 | 30 | for (int i = 0; i < bitbuffer->num_rows; ++i) { 31 | if (bitbuffer->bits_per_row[i] != 33) // last row is 32 32 | continue; // DECODE_ABORT_LENGTH 33 | 34 | b = bitbuffer->bb[i]; 35 | 36 | //This needs additional validation, but works on mine. Suspect each DC5-UK-WH uses different codes as the transmitter 37 | //is paired to the receivers to avoid being triggered by the neighbours transmitter ?!? 38 | // TODO: cleaner implementation with 2 preamble arrays 39 | if (((b[0] != 0xce) || (b[1] != 0x8e) || (b[2] != 0x2a) || (b[3] != 0x6c) || (b[4] != 0x80)) && 40 | ((b[0] != 0xe7) || (b[1] != 0x37) || (b[2] != 0x7a) || (b[3] != 0x2c) || (b[4] != 0x80))) 41 | continue; // DECODE_ABORT_EARLY 42 | 43 | sprintf(id_str, "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]); 44 | 45 | data = data_make( 46 | "model", "", DATA_STRING, "Blyss-DC5ukwh", 47 | "id", "", DATA_STRING, id_str, 48 | NULL); 49 | decoder_output_data(decoder, data); 50 | 51 | return 1; 52 | } 53 | 54 | return DECODE_FAIL_SANITY; 55 | } 56 | 57 | static char *output_fields[] = { 58 | "model", 59 | "id", 60 | NULL 61 | }; 62 | 63 | r_device blyss = { 64 | .name = "Blyss DC5-UK-WH", 65 | .modulation = OOK_PULSE_PWM, 66 | .short_width = 500, 67 | .long_width = 1500, 68 | .gap_limit = 2500, 69 | .reset_limit = 8000, 70 | .decode_fn = &blyss_callback, 71 | .disabled = 0, 72 | .fields = output_fields, 73 | }; 74 | -------------------------------------------------------------------------------- /src/rtl_433/devices/generic_temperature_sensor.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Generic temperature sensor 1. 3 | 4 | Copyright (C) 2015 Alexandre Coffignal 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Generic temperature sensor 1. 13 | 14 | 10 24 bits frames: 15 | 16 | IIIIIIII BBTTTTTT TTTTTTTT 17 | 18 | - I: 8 bit ID 19 | - B: 2 bit? Battery ? 20 | - T: 12 bit Temp 21 | */ 22 | 23 | #include "decoder.h" 24 | 25 | static int generic_temperature_sensor_callback(r_device *decoder, bitbuffer_t *bitbuffer) 26 | { 27 | data_t *data; 28 | uint8_t *b = bitbuffer->bb[1]; 29 | int i, device, battery, temp_raw; 30 | float temp_f; 31 | 32 | for (i = 1; i < 10; i++) { 33 | if (bitbuffer->bits_per_row[i] != 24) { 34 | /*10 24 bits frame*/ 35 | return DECODE_ABORT_LENGTH; 36 | } 37 | } 38 | 39 | // reduce false positives 40 | if ((b[0] == 0 && b[1] == 0 && b[2] == 0) 41 | || (b[0] == 0xff && b[1] == 0xff && b[2] == 0xff)) { 42 | return DECODE_ABORT_EARLY; 43 | } 44 | 45 | device = (b[0]); 46 | battery = (b[1] & 0xC0) >> 6; 47 | temp_raw = (int16_t)(((b[1] & 0x3f) << 10) | (b[2] << 2)); 48 | temp_f = (temp_raw >> 4) * 0.1f; 49 | 50 | /* clang-format off */ 51 | data = data_make( 52 | "model", "", DATA_STRING, "Generic-Temperature", 53 | "id", "Id", DATA_INT, device, 54 | "battery_ok", "Battery?", DATA_INT, battery, 55 | "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_f, 56 | NULL); 57 | /* clang-format on */ 58 | 59 | decoder_output_data(decoder, data); 60 | return 1; 61 | } 62 | 63 | static char *output_fields[] = { 64 | "model", 65 | "id", 66 | "battery_ok", 67 | "temperature_C", 68 | NULL, 69 | }; 70 | 71 | r_device generic_temperature_sensor = { 72 | .name = "Generic temperature sensor 1", 73 | .modulation = OOK_PULSE_PPM, 74 | .short_width = 2000, 75 | .long_width = 4000, 76 | .gap_limit = 4800, 77 | .reset_limit = 10000, 78 | .decode_fn = &generic_temperature_sensor_callback, 79 | .disabled = 0, 80 | .fields = output_fields, 81 | }; 82 | -------------------------------------------------------------------------------- /tools/rtl_433_devices.fragment: -------------------------------------------------------------------------------- 1 | DECL(acurite_rain_896) \ 2 | DECL(acurite_th) \ 3 | DECL(acurite_txr) \ 4 | DECL(acurite_986) \ 5 | DECL(acurite_606) \ 6 | DECL(acurite_00275rm) \ 7 | DECL(acurite_590tx) \ 8 | DECL(acurite_01185m) \ 9 | DECL(akhan_100F14) \ 10 | DECL(alectov1) \ 11 | DECL(ambientweather_tx8300) \ 12 | DECL(auriol_4ld5661) \ 13 | DECL(auriol_aft77b2) \ 14 | DECL(auriol_afw2a1) \ 15 | DECL(auriol_ahfl) \ 16 | DECL(auriol_hg02832) \ 17 | DECL(blueline) \ 18 | DECL(blyss) \ 19 | DECL(brennenstuhl_rcs_2044) \ 20 | DECL(bresser_3ch) \ 21 | DECL(bt_rain) \ 22 | DECL(burnhardbbq) \ 23 | DECL(calibeur_RF104) \ 24 | DECL(cardin) \ 25 | DECL(chuango) \ 26 | DECL(companion_wtr001) \ 27 | DECL(digitech_xc0324) \ 28 | DECL(dish_remote_6_3) \ 29 | DECL(ecowitt) \ 30 | DECL(eurochron_efth800) \ 31 | DECL(elro_db286a) \ 32 | DECL(elv_em1000) \ 33 | DECL(elv_ws2000) \ 34 | DECL(esperanza_ews) \ 35 | DECL(eurochron) \ 36 | DECL(fineoffset_WH2) \ 37 | DECL(fineoffset_WH0530) \ 38 | DECL(fineoffset_wh1050) \ 39 | DECL(fineoffset_wh1080) \ 40 | DECL(fs20) \ 41 | DECL(ft004b) \ 42 | DECL(generic_motion) \ 43 | DECL(generic_remote) \ 44 | DECL(generic_temperature_sensor) \ 45 | DECL(govee) \ 46 | DECL(gt_tmbbq05) \ 47 | DECL(gt_wt_02) \ 48 | DECL(gt_wt_03) \ 49 | DECL(hcs200) \ 50 | DECL(honeywell_wdb) \ 51 | DECL(ht680) \ 52 | DECL(infactory) \ 53 | DECL(kw9015b) \ 54 | DECL(interlogix) \ 55 | DECL(intertechno) \ 56 | DECL(kedsum) \ 57 | DECL(kerui) \ 58 | DECL(lacrossetx) \ 59 | DECL(lacrosse_tx141x) \ 60 | DECL(lacrosse_ws7000) \ 61 | DECL(lacrossews) \ 62 | DECL(lightwave_rf) \ 63 | DECL(markisol) \ 64 | DECL(maverick_et73) \ 65 | DECL(mebus433) \ 66 | DECL(missil_ml0757) \ 67 | DECL(new_template) \ 68 | DECL(nexus) \ 69 | DECL(nice_flor_s) \ 70 | DECL(opus_xt300) \ 71 | DECL(oregon_scientific_sl109h) \ 72 | DECL(oregon_scientific_v1) \ 73 | DECL(philips_aj3650) \ 74 | DECL(philips_aj7010) \ 75 | DECL(prologue) \ 76 | DECL(quhwa) \ 77 | DECL(rftech) \ 78 | DECL(rubicson) \ 79 | DECL(rubicson_48659) \ 80 | DECL(s3318p) \ 81 | DECL(silvercrest) \ 82 | DECL(skylink_motion) \ 83 | DECL(smoke_gs558) \ 84 | DECL(solight_te44) \ 85 | DECL(springfield) \ 86 | DECL(tfa_30_3221) \ 87 | DECL(tfa_drop_303233) \ 88 | DECL(tfa_pool_thermometer) \ 89 | DECL(tfa_twin_plus_303049) \ 90 | DECL(thermopro_tp11) \ 91 | DECL(thermopro_tp12) \ 92 | DECL(thermopro_tx2) \ 93 | DECL(ts_ft002) \ 94 | DECL(visonic_powercode) \ 95 | DECL(waveman) \ 96 | DECL(wg_pb12v1) \ 97 | DECL(ws2032) \ 98 | DECL(wssensor) \ 99 | DECL(wt1024) \ 100 | DECL(X10_RF) \ 101 | DECL(x10_sec) \ 102 | /* Add new decoders here. */ 103 | #define NUMOFDEVICES 101 104 | -------------------------------------------------------------------------------- /src/rtl_433/devices/ft004b.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | FT-004-B Temperature Sensor. 3 | 4 | Copyright (C) 2017 George Hopkins 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | FT-004-B Temperature Sensor. 13 | 14 | The sensor sends a packet every 60 seconds. Each frame of 46 bits 15 | is sent 3 times without padding/pauses. 16 | 17 | Format: FFFFFFFF ???????? ???????? tttttttt TTT????? ?????? 18 | Fixed type code: 0xf4, Temperature (t=lsb, T=msb), Unknown (?) 19 | 20 | {137} 2f cf 24 78 21 c8 bf 3c 91 e0 87 22 fc f2 47 82 1c 80 21 | {137} 2f ce 24 72 a1 70 bf 38 91 ca 85 c2 fc e2 47 2a 17 00 22 | 23 | Aligning at [..] (insert 2 bits) we get: 24 | 25 | 2f cf 24 78 21 c8 [..] 2f cf 24 78 21 c8 [..] 2f cf 24 78 21 c8 26 | 2f ce 24 72 a1 70 [..] 2f ce 24 72 a1 70 [..] 2f ce 24 72 a1 70 27 | 28 | */ 29 | 30 | #include "decoder.h" 31 | 32 | static int ft004b_callback(r_device *decoder, bitbuffer_t *bitbuffer) 33 | { 34 | uint8_t* msg; 35 | float temperature; 36 | data_t *data; 37 | 38 | if (bitbuffer->bits_per_row[0] != 137 && bitbuffer->bits_per_row[0] != 138) { 39 | return DECODE_ABORT_LENGTH; 40 | } 41 | 42 | /* take the majority of all 46 bits (pattern is sent 3 times) and reverse them */ 43 | msg = bitbuffer->bb[0]; 44 | for (int i = 0; i < (46 + 7) / 8; i++) { 45 | uint8_t a = bitrow_get_byte(msg, i * 8); 46 | uint8_t b = bitrow_get_byte(msg, i * 8 + 46); 47 | uint8_t c = bitrow_get_byte(msg, i * 8 + 46 * 2); 48 | msg[i] = reverse8((a & b) | (b & c) | (a & c)); 49 | } 50 | 51 | if (msg[0] != 0xf4) 52 | return DECODE_FAIL_SANITY; 53 | 54 | int temp_raw = ((msg[4] & 0x7) << 8) | msg[3]; 55 | temperature = (temp_raw * 0.05f) - 40.0f; 56 | 57 | /* clang-format off */ 58 | data = data_make( 59 | "model", "", DATA_STRING, "FT-004B", 60 | "temperature_C", "Temperature", DATA_FORMAT, "%.1f", DATA_DOUBLE, temperature, 61 | NULL); 62 | /* clang-format on */ 63 | decoder_output_data(decoder, data); 64 | 65 | return 1; 66 | } 67 | 68 | static char *output_fields[] = { 69 | "model", 70 | "temperature_C", 71 | NULL, 72 | }; 73 | 74 | r_device ft004b = { 75 | .name = "FT-004-B Temperature Sensor", 76 | .modulation = OOK_PULSE_PPM, 77 | .short_width = 1956, 78 | .long_width = 3900, 79 | .gap_limit = 4000, 80 | .reset_limit = 4000, 81 | .decode_fn = &ft004b_callback, 82 | .disabled = 0, 83 | .fields = output_fields, 84 | }; 85 | -------------------------------------------------------------------------------- /src/rtl_433/devices/rftech.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | RF-tech decoder (INFRA 217S34). 3 | 4 | Copyright (C) 2016 Erik Johannessen 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | RF-tech decoder (INFRA 217S34). 13 | 14 | Also marked INFRA 217S34 15 | Ewig Industries Macao 16 | 17 | Example of message: 18 | 19 | 01001001 00011010 00000100 20 | 21 | - First byte is unknown, but probably id. 22 | - Second byte is the integer part of the temperature. 23 | - Third byte bits 0-3 is the fraction/tenths of the temperature. 24 | - Third byte bit 7 is 1 with fresh batteries. 25 | - Third byte bit 6 is 1 on button press. 26 | 27 | More sample messages: 28 | 29 | {24} ad 18 09 : 10101101 00011000 00001001 30 | {24} 3e 17 09 : 00111110 00010111 00001001 31 | {24} 70 17 03 : 01110000 00010111 00000011 32 | {24} 09 17 01 : 00001001 00010111 00000001 33 | 34 | With fresh batteries and button pressed: 35 | 36 | {24} c5 16 c5 : 11000101 00010110 11000101 37 | 38 | */ 39 | 40 | #include "decoder.h" 41 | 42 | static int rftech_callback(r_device *decoder, bitbuffer_t *bitbuffer) 43 | { 44 | int r = bitbuffer_find_repeated_row(bitbuffer, 3, 24); 45 | 46 | if (r < 0 || bitbuffer->bits_per_row[r] != 24) 47 | return DECODE_ABORT_LENGTH; 48 | uint8_t *b = bitbuffer->bb[r]; 49 | 50 | int sensor_id = b[0]; 51 | float temp_c = (b[1] & 0x7f) + (b[2] & 0x0f) * 0.1f; 52 | if (b[1] & 0x80) 53 | temp_c = -temp_c; 54 | 55 | int battery = (b[2] & 0x80) == 0x80; 56 | int button = (b[2] & 0x60) != 0; 57 | 58 | /* clang-format off */ 59 | data_t *data = data_make( 60 | "model", "", DATA_STRING, "RF-tech", 61 | "id", "Id", DATA_INT, sensor_id, 62 | "battery_ok", "Battery", DATA_INT, battery, 63 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, 64 | "button", "Button", DATA_INT, button, 65 | NULL); 66 | /* clang-format on */ 67 | 68 | decoder_output_data(decoder, data); 69 | return 1; 70 | } 71 | 72 | static char *csv_output_fields[] = { 73 | "model", 74 | "id", 75 | "battery_ok", 76 | "temperature_C", 77 | "button", 78 | NULL, 79 | }; 80 | 81 | r_device rftech = { 82 | .name = "RF-tech", 83 | .modulation = OOK_PULSE_PPM, 84 | .short_width = 2000, 85 | .long_width = 4000, 86 | .gap_limit = 5000, 87 | .reset_limit = 10000, 88 | .decode_fn = &rftech_callback, 89 | .disabled = 1, 90 | .fields = csv_output_fields, 91 | }; 92 | -------------------------------------------------------------------------------- /src/rtl_433/devices/thermopro_tp11.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Thermopro TP-11 Thermometer. 3 | 4 | Copyright (C) 2017 Google Inc. 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Thermopro TP-11 Thermometer. 13 | 14 | normal sequence of bit rows: 15 | 16 | [00] {33} db 41 57 c2 80 : 11011011 01000001 01010111 11000010 1 17 | [01] {33} db 41 57 c2 80 : 11011011 01000001 01010111 11000010 1 18 | [02] {33} db 41 57 c2 80 : 11011011 01000001 01010111 11000010 1 19 | [03] {32} db 41 57 c2 : 11011011 01000001 01010111 11000010 20 | 21 | */ 22 | #include "decoder.h" 23 | 24 | static int thermopro_tp11_sensor_callback(r_device *decoder, bitbuffer_t *bitbuffer) 25 | { 26 | // Compare first four bytes of rows that have 32 or 33 bits. 27 | int row = bitbuffer_find_repeated_row(bitbuffer, 2, 32); 28 | if (row < 0) 29 | return DECODE_ABORT_EARLY; 30 | uint8_t *b = bitbuffer->bb[row]; 31 | 32 | if (bitbuffer->bits_per_row[row] > 33) 33 | return DECODE_ABORT_LENGTH; 34 | 35 | uint8_t ic = lfsr_digest8_reflect(b, 3, 0x51, 0x04); 36 | if (ic != b[3]) { 37 | return DECODE_FAIL_MIC; 38 | } 39 | 40 | // No need to decode/extract values for simple test 41 | if ( (!b[0] && !b[1] && !b[2] && !b[3]) 42 | || (b[0] == 0xff && b[1] == 0xff && b[2] == 0xff && b[3] == 0xff)) { 43 | if (decoder->verbose > 1) { 44 | fprintf(stderr, "%s: DECODE_FAIL_SANITY data all 0x00 or 0xFF\n", __func__); 45 | } 46 | return DECODE_FAIL_SANITY; 47 | } 48 | 49 | int device = (b[0] << 4) | (b[1] >> 4); 50 | int temp_raw = ((b[1] & 0x0f) << 8) | b[2]; 51 | float temp_c = (temp_raw - 200) * 0.1f; 52 | 53 | /* clang-format off */ 54 | data_t *data = data_make( 55 | "model", "", DATA_STRING, "Thermopro-TP11", 56 | "id", "Id", DATA_INT, device, 57 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, 58 | "mic", "Integrity", DATA_STRING, "CRC", 59 | NULL); 60 | /* clang-format on */ 61 | decoder_output_data(decoder, data); 62 | return 1; 63 | } 64 | 65 | static char *output_fields[] = { 66 | "model", 67 | "id", 68 | "temperature_C", 69 | "mic", 70 | NULL, 71 | }; 72 | 73 | r_device thermopro_tp11 = { 74 | .name = "Thermopro TP11 Thermometer", 75 | .modulation = OOK_PULSE_PPM, 76 | .short_width = 500, 77 | .long_width = 1500, 78 | .gap_limit = 2000, 79 | .reset_limit = 4000, 80 | .decode_fn = &thermopro_tp11_sensor_callback, 81 | .disabled = 0, 82 | .fields = output_fields, 83 | }; 84 | -------------------------------------------------------------------------------- /src/rtl_433/devices/generic_motion.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Generic off-brand wireless motion sensor and alarm system on 433.3MHz. 3 | 4 | Copyright (C) 2015 Christian W. Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Generic off-brand wireless motion sensor and alarm system on 433.3MHz. 13 | 14 | Example codes are: 80042 Arm alarm, 80002 Disarm alarm, 15 | 80008 System ping (every 15 minutes), 800a2, 800c2, 800e2 Motion event 16 | (following motion detection the sensor will blackout for 90 seconds). 17 | 18 | 2315 baud on/off rate and alternating 579 baud bit rate and 463 baud bit rate 19 | Each transmission has a warmup of 17 to 32 pulse widths then 8 packets with 20 | alternating 1:3 / 2:2 or 1:4 / 2:3 gap:pulse ratio for 0/1 bit in the packet 21 | with a repeat gap of 4 pulse widths, i.e.: 22 | - 6704 us to 13092 us warmup pulse, 1672 us gap, 23 | - 0: 472 us gap, 1332 us pulse 24 | - 1: 920 us gap, 888 us pulse 25 | - 1672 us repeat gap, 26 | - 0: 472 us gap, 1784 us pulse 27 | - 1: 920 us gap, 1332 us pulse 28 | - ... 29 | */ 30 | 31 | #include "decoder.h" 32 | 33 | static int generic_motion_callback(r_device *decoder, bitbuffer_t *bitbuffer) 34 | { 35 | data_t *data; 36 | uint8_t *b; 37 | int code; 38 | char code_str[6]; 39 | 40 | for (int i = 0; i < bitbuffer->num_rows; ++i) { 41 | b = bitbuffer->bb[i]; 42 | // strictly validate package as there is no checksum 43 | if ((bitbuffer->bits_per_row[i] != 20) 44 | || ((b[1] == 0) && (b[2] == 0)) 45 | || ((b[1] == 0xff) && (b[2] == 0xff)) 46 | || count_repeats(bitbuffer, i) < 3) 47 | continue; // DECODE_ABORT_EARLY 48 | 49 | code = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); 50 | sprintf(code_str, "%05x", code); 51 | 52 | /* clang-format off */ 53 | data = data_make( 54 | "model", "", DATA_STRING, "Generic-Motion", 55 | "code", "", DATA_STRING, code_str, 56 | NULL); 57 | /* clang-format on */ 58 | 59 | decoder_output_data(decoder, data); 60 | return 1; 61 | } 62 | return DECODE_ABORT_EARLY; 63 | } 64 | 65 | static char *output_fields[] = { 66 | "model", 67 | "code", 68 | NULL, 69 | }; 70 | 71 | r_device generic_motion = { 72 | .name = "Generic wireless motion sensor", 73 | .modulation = OOK_PULSE_PWM, 74 | .short_width = 888, 75 | .long_width = (1332 + 1784) / 2, 76 | .sync_width = 1784 + 670, 77 | .gap_limit = 1200, 78 | .reset_limit = 2724 * 1.5, 79 | .decode_fn = &generic_motion_callback, 80 | .disabled = 0, 81 | .fields = output_fields, 82 | }; 83 | -------------------------------------------------------------------------------- /src/rtl_433/devices/auriol_4ld5661.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Auriol 4-LD5661 sensor. 3 | 4 | Copyright (C) 2021 Balazs H. 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | /** 13 | Lidl Auriol 4-LD5661 sensor. 14 | 15 | See also issue #1857. 16 | 17 | Data layout: 18 | 19 | II B TTT F RRRRRR 20 | 21 | - I: id, 8 bit: what we've seen so far are 1a, c6 22 | - B: battery, 4 bit: 0x8 if normal, 0x0 if low 23 | - T: temperature, 12 bit: 2's complement, scaled by 10 24 | - F: 4 bit: seems to be 0xf constantly, a separator between temp and rain 25 | - R: rain sensor, probably the remaining 24 bit: a counter for every 0.3mm of rain 26 | 27 | */ 28 | 29 | #include "decoder.h" 30 | 31 | static int auriol_4ld5661_decode(r_device *decoder, bitbuffer_t *bitbuffer) 32 | { 33 | int ret = 0; 34 | 35 | for (int i = 0; i < bitbuffer->num_rows; i++) { 36 | if (bitbuffer->bits_per_row[i] != 52) { 37 | ret = DECODE_ABORT_LENGTH; 38 | continue; 39 | } 40 | 41 | uint8_t *b = bitbuffer->bb[i]; 42 | int id = b[0]; 43 | int batt_ok = b[1] >> 7; 44 | 45 | if (b[3] != 0xf0 || (b[1] & 0x70) != 0) { 46 | ret = DECODE_FAIL_MIC; 47 | continue; 48 | } 49 | 50 | int temp_raw = (int16_t)(((b[1] & 0x0f) << 12) | (b[2] << 4)); // uses sign extend 51 | float temp_c = (temp_raw >> 4) * 0.1F; 52 | 53 | int rain_raw = (b[4] << 12) | (b[5] << 4) | b[6] >> 4; 54 | float rain = rain_raw * 0.3F; 55 | 56 | /* clang-format off */ 57 | data_t *data = data_make( 58 | "model", "Model", DATA_STRING, "Auriol-4LD5661", 59 | "id", "ID", DATA_FORMAT, "%02x", DATA_INT, id, 60 | "battery_ok", "Battery OK", DATA_INT, batt_ok, 61 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, 62 | "rain_mm", "Rain", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain, 63 | NULL); 64 | /* clang-format on */ 65 | 66 | decoder_output_data(decoder, data); 67 | return 1; 68 | } 69 | 70 | return ret; 71 | } 72 | 73 | static char *output_fields[] = { 74 | "model", 75 | "id", 76 | "battery_ok", 77 | "temperature_C", 78 | "rain_mm", 79 | NULL, 80 | }; 81 | 82 | r_device auriol_4ld5661 = { 83 | .name = "Auriol 4-LD5661 temperature/rain sensor", 84 | .modulation = OOK_PULSE_PPM, 85 | .short_width = 1000, 86 | .long_width = 2000, 87 | .sync_width = 2500, 88 | .gap_limit = 2500, 89 | .reset_limit = 4000, 90 | .decode_fn = &auriol_4ld5661_decode, 91 | .disabled = 1, // no sync-word, no fix id, no checksum 92 | .fields = output_fields, 93 | }; 94 | -------------------------------------------------------------------------------- /src/rtl_433/devices/generic_remote.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Generic remotes and sensors using PT2260/PT2262 SC2260/SC2262 EV1527 protocol. 3 | 4 | Copyright (C) 2015 Tommy Vestermark 5 | Copyright (C) 2015 nebman 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | */ 12 | /** 13 | Generic remotes and sensors using PT2260/PT2262 SC2260/SC2262 EV1527 protocol. 14 | 15 | Tested devices: 16 | - SC2260 17 | - EV1527 18 | */ 19 | 20 | #include "decoder.h" 21 | 22 | static int generic_remote_callback(r_device *decoder, bitbuffer_t *bitbuffer) 23 | { 24 | data_t *data; 25 | uint8_t *b = bitbuffer->bb[0]; 26 | char tristate[23]; 27 | char *p = tristate; 28 | 29 | //invert bits, short pulse is 0, long pulse is 1 30 | b[0] = ~b[0]; 31 | b[1] = ~b[1]; 32 | b[2] = ~b[2]; 33 | 34 | unsigned bits = bitbuffer->bits_per_row[0]; 35 | 36 | // Validate package 37 | if ((bits != 25) 38 | || (b[3] & 0x80) == 0 // Last bit (MSB here) is always 1 39 | || (b[0] == 0 && b[1] == 0) // Reduce false positives. ID 0x0000 not supported 40 | || (b[2] == 0)) // Reduce false positives. CMD 0x00 not supported 41 | return DECODE_ABORT_LENGTH; 42 | 43 | int id_16b = b[0] << 8 | b[1]; 44 | int cmd_8b = b[2]; 45 | 46 | // output tristate coding 47 | uint32_t full = b[0] << 16 | b[1] << 8 | b[2]; 48 | 49 | for (int i = 22; i >= 0; i -= 2) { 50 | switch ((full>>i) & 0x03) { 51 | case 0x00: *p++ = '0'; break; 52 | case 0x01: *p++ = 'Z'; break; // floating / "open" 53 | case 0x02: *p++ = 'X'; break; // tristate 10 is invalid code for SC226x but valid in EV1527 54 | case 0x03: *p++ = '1'; break; 55 | default: *p++ = '?'; break; // not possible anyway 56 | } 57 | } 58 | *p = '\0'; 59 | 60 | /* clang-format off */ 61 | data = data_make( 62 | "model", "", DATA_STRING, "Generic-Remote", 63 | "id", "House Code", DATA_INT, id_16b, 64 | "cmd", "Command", DATA_INT, cmd_8b, 65 | "tristate", "Tri-State", DATA_STRING, tristate, 66 | NULL); 67 | /* clang-format on */ 68 | 69 | decoder_output_data(decoder, data); 70 | 71 | return 1; 72 | } 73 | 74 | static char *output_fields[] = { 75 | "model", 76 | "id", 77 | "cmd", 78 | "tristate", 79 | NULL, 80 | }; 81 | 82 | r_device generic_remote = { 83 | .name = "Generic Remote SC226x EV1527", 84 | .modulation = OOK_PULSE_PWM, 85 | .short_width = 464, 86 | .long_width = 1404, 87 | .reset_limit = 1800, 88 | .sync_width = 0, // No sync bit used 89 | .tolerance = 200, // us 90 | .decode_fn = &generic_remote_callback, 91 | .disabled = 0, 92 | .fields = output_fields, 93 | }; 94 | -------------------------------------------------------------------------------- /src/rtl_433/devices/intertechno.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Intertechno remotes. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | */ 10 | /** 11 | Intertechno remotes. 12 | 13 | Intertechno remote labeled ITT-1500 that came with 3x ITR-1500 remote outlets. The set is labeled IT-1500. 14 | The PPM consists of a 220µs high followed by 340µs or 1400µs of gap. 15 | 16 | There is another type of remotes that have an ID prefix of 0x56 and slightly shorter timing. 17 | 18 | */ 19 | 20 | #include "decoder.h" 21 | 22 | static int intertechno_callback(r_device *decoder, bitbuffer_t *bitbuffer) 23 | { 24 | data_t *data; 25 | bitrow_t *bb = bitbuffer->bb; 26 | uint8_t *b = bitbuffer->bb[1]; 27 | char id_str[11]; 28 | int slave; 29 | int master; 30 | int command; 31 | 32 | if (bb[0][0] != 0 || (bb[1][0] != 0x56 && bb[1][0] != 0x69)) 33 | return DECODE_ABORT_EARLY; 34 | 35 | if (decoder->verbose > 1) { 36 | fprintf(stderr, "Switch event:\n"); 37 | fprintf(stderr, "protocol = Intertechno\n"); 38 | fprintf(stderr, "rid = %x\n", b[0]); 39 | fprintf(stderr, "rid = %x\n", b[1]); 40 | fprintf(stderr, "rid = %x\n", b[2]); 41 | fprintf(stderr, "rid = %x\n", b[3]); 42 | fprintf(stderr, "rid = %x\n", b[4]); 43 | fprintf(stderr, "rid = %x\n", b[5]); 44 | fprintf(stderr, "rid = %x\n", b[6]); 45 | fprintf(stderr, "rid = %x\n", b[7]); 46 | fprintf(stderr, "ADDR Slave = %i\n", b[7] & 0x0f); 47 | fprintf(stderr, "ADDR Master = %i\n",( b[7] & 0xf0) >> 4); 48 | fprintf(stderr, "command = %i\n",( b[6] & 0x07)); 49 | } 50 | 51 | sprintf(id_str, "%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4]); 52 | slave = b[7] & 0x0f; 53 | master = (b[7] & 0xf0) >> 4; 54 | command = b[6] & 0x07; 55 | 56 | /* clang-format off */ 57 | data = data_make( 58 | "model", "", DATA_STRING, "Intertechno-Remote", 59 | "id", "", DATA_STRING, id_str, 60 | "slave", "", DATA_INT, slave, 61 | "master", "", DATA_INT, master, 62 | "command", "", DATA_INT, command, 63 | NULL); 64 | /* clang-format on */ 65 | 66 | decoder_output_data(decoder, data); 67 | return 1; 68 | } 69 | 70 | static char *output_fields[] = { 71 | "model", 72 | "type", 73 | "id", 74 | "slave", 75 | "master", 76 | "command", 77 | NULL, 78 | }; 79 | 80 | r_device intertechno = { 81 | .name = "Intertechno 433", 82 | .modulation = OOK_PULSE_PPM, 83 | .short_width = 330, 84 | .long_width = 1400, 85 | .gap_limit = 1700, 86 | .reset_limit = 10000, 87 | .decode_fn = &intertechno_callback, 88 | .disabled = 1, 89 | .fields = output_fields, 90 | }; 91 | -------------------------------------------------------------------------------- /src/rtl_433/devices/mebus.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Mebus 433. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | */ 9 | 10 | #include "decoder.h" 11 | 12 | static int mebus433_callback(r_device *decoder, bitbuffer_t *bitbuffer) 13 | { 14 | bitrow_t *bb = bitbuffer->bb; 15 | int16_t temp; 16 | int8_t hum; 17 | uint8_t address; 18 | uint8_t channel; 19 | uint8_t battery; 20 | uint8_t unknown1; 21 | uint8_t unknown2; 22 | data_t *data; 23 | 24 | // TODO: missing packet length validation 25 | 26 | if (bb[0][0] == 0 && bb[1][4] !=0 && (bb[1][0] & 0x60) && bb[1][3]==bb[5][3] && bb[1][4] == bb[12][4]) { 27 | 28 | address = bb[1][0] & 0x1f; 29 | 30 | channel = ((bb[1][1] & 0x30) >> 4) + 1; 31 | // Always 0? 32 | unknown1 = (bb[1][1] & 0x40) >> 6; 33 | battery = bb[1][1] & 0x80; 34 | 35 | // Upper 4 bits are stored in nibble 1, lower 8 bits are stored in nibble 2 36 | // upper 4 bits of nibble 1 are reserved for other usages. 37 | temp = (int16_t)((uint16_t)(bb[1][1] << 12) | bb[1][2] << 4); 38 | temp = temp >> 4; 39 | // lower 4 bits of nibble 3 and upper 4 bits of nibble 4 contains 40 | // humidity as decimal value 41 | hum = (bb[1][3] << 4 | bb[1][4] >> 4); 42 | 43 | // Always 0b1111? 44 | unknown2 = (bb[1][3] & 0xf0) >> 4; 45 | 46 | /* clang-format off */ 47 | data = data_make( 48 | "model", "", DATA_STRING, "Mebus-433", 49 | "id", "Address", DATA_INT, address, 50 | "channel", "Channel", DATA_INT, channel, 51 | "battery_ok", "Battery", DATA_INT, !!battery, 52 | "unknown1", "Unknown 1", DATA_INT, unknown1, 53 | "unknown2", "Unknown 2", DATA_INT, unknown2, 54 | "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp * 0.1f, 55 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, hum, 56 | NULL); 57 | /* clang-format on */ 58 | 59 | decoder_output_data(decoder, data); 60 | return 1; 61 | } 62 | return DECODE_ABORT_EARLY; 63 | } 64 | 65 | static char *output_fields[] = { 66 | "model", 67 | "id", 68 | "channel", 69 | "battery_ok", 70 | "unknown1", 71 | "unknown2", 72 | "temperature_C", 73 | "humidity", 74 | NULL, 75 | }; 76 | 77 | r_device mebus433 = { 78 | .name = "Mebus 433", 79 | .modulation = OOK_PULSE_PPM, 80 | .short_width = 800, // guessed, no samples available 81 | .long_width = 1600, // guessed, no samples available 82 | .gap_limit = 2400, 83 | .reset_limit = 6000, 84 | .decode_fn = &mebus433_callback, 85 | .disabled = 1, // add docs, tests, false positive checks and then reenable 86 | .fields = output_fields, 87 | }; 88 | -------------------------------------------------------------------------------- /src/rtl_433/devices/nice_flor_s.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Nice Flor-s remote for gates. 3 | 4 | Copyright (C) 2020 Samuel Tardieu 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | /** 13 | Nice Flor-s remote for gates. 14 | 15 | Protocol description: 16 | The protocol has been analyzed at this link: http://phreakerclub.com/1615 17 | 18 | A packet is made of 52 bits (13 nibbles S0 to S12): 19 | 20 | - S0: button ID from 1 to 4 (or 1 to 2 depending on the remote) 21 | - S1: retransmission count starting from 1, xored with ~S0 22 | - S2 and S7-S12: 28 bit encrypted serial number 23 | - S3-S6: 16 bits encrypted rolling code 24 | */ 25 | 26 | #include "decoder.h" 27 | 28 | static int nice_flor_s_decode(r_device *decoder, bitbuffer_t *bitbuffer) 29 | { 30 | if (bitbuffer->num_rows != 2 || bitbuffer->bits_per_row[1] != 0) { 31 | return DECODE_ABORT_EARLY; 32 | } 33 | if (bitbuffer->bits_per_row[0] != 52) { 34 | return DECODE_ABORT_LENGTH; 35 | } 36 | 37 | bitbuffer_invert(bitbuffer); 38 | uint8_t *b = bitbuffer->bb[0]; 39 | 40 | uint8_t button_id = b[0] >> 4; 41 | if (button_id < 1 || button_id > 4) { 42 | return DECODE_ABORT_EARLY; 43 | } 44 | int count = 1 + (((b[0] ^ ~button_id) - 1) & 0xf); 45 | uint32_t serial = ((b[1] & 0xf0) << 20) | ((b[3] & 0xf) << 20) | 46 | (b[4] << 12) | (b[5] << 4) | (b[6] >> 4); 47 | uint16_t code = (b[1] << 12) | (b[2] << 4) | (b[3] >> 4); 48 | 49 | /* clang-format off */ 50 | data_t *data = data_make( 51 | "model", "", DATA_STRING, "Nice-FlorS", 52 | "button", "Button ID", DATA_INT, button_id, 53 | "serial", "Serial (enc.)", DATA_FORMAT, "%07x", DATA_INT, serial, 54 | "code", "Code (enc.)", DATA_FORMAT, "%04x", DATA_INT, code, 55 | "count", "", DATA_INT, count, 56 | NULL); 57 | /* clang-format on */ 58 | 59 | decoder_output_data(decoder, data); 60 | return 1; 61 | } 62 | 63 | static char *output_fields[] = { 64 | "model", 65 | "button", 66 | "serial", 67 | "code", 68 | "count", 69 | NULL, 70 | }; 71 | 72 | // Example: 73 | // $ rtl_433 -R 169 -y "{52} 0xe7a760b94372e {0}" 74 | // time : 2020-10-21 11:06:12 75 | // model : Nice Flor-s Button ID : 1 Serial (enc.): 56bc8d1 Code (enc.): 89f4 76 | // count : 6 77 | 78 | r_device nice_flor_s = { 79 | .name = "Nice Flor-s remote control for gates", 80 | .modulation = OOK_PULSE_PWM, 81 | .short_width = 500, // short pulse is ~500 us + ~1000 us gap 82 | .long_width = 1000, // long pulse is ~1000 us + ~500 us gap 83 | .sync_width = 1500, // sync pulse is ~1500 us + ~1500 us gap 84 | .gap_limit = 2000, 85 | .reset_limit = 5000, 86 | .tolerance = 100, 87 | .decode_fn = &nice_flor_s_decode, 88 | .disabled = 1, 89 | .fields = output_fields, 90 | }; 91 | -------------------------------------------------------------------------------- /include/r_device.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | Definition of r_device struct. 3 | */ 4 | 5 | #ifndef INCLUDE_R_DEVICE_H_ 6 | #define INCLUDE_R_DEVICE_H_ 7 | 8 | /** Supported modulation types. */ 9 | enum modulation_types { 10 | OOK_PULSE_MANCHESTER_ZEROBIT = 3, ///< Manchester encoding. Hardcoded zerobit. Rising Edge = 0, Falling edge = 1. 11 | OOK_PULSE_PCM_RZ = 4, ///< Pulse Code Modulation with Return-to-Zero encoding, Pulse = 0, No pulse = 1. 12 | OOK_PULSE_PPM = 5, ///< Pulse Position Modulation. Short gap = 0, Long = 1. 13 | OOK_PULSE_PWM = 6, ///< Pulse Width Modulation with precise timing parameters. 14 | OOK_PULSE_PIWM_RAW = 8, ///< Level shift for each bit. Short interval = 1, Long = 0. 15 | OOK_PULSE_PIWM_DC = 11, ///< Level shift for each bit. Short interval = 1, Long = 0. 16 | OOK_PULSE_DMC = 9, ///< Level shift within the clock cycle. 17 | OOK_PULSE_PWM_OSV1 = 10, ///< Pulse Width Modulation. Oregon Scientific v1. 18 | OOK_PULSE_NRZS = 12, ///< NRZS modulation 19 | FSK_DEMOD_MIN_VAL = 16, ///< Dummy. FSK demodulation must start at this value. 20 | FSK_PULSE_PCM = 16, ///< FSK, Pulse Code Modulation. 21 | FSK_PULSE_PWM = 17, ///< FSK, Pulse Width Modulation. Short pulses = 1, Long = 0. 22 | FSK_PULSE_MANCHESTER_ZEROBIT = 18, ///< FSK, Manchester encoding. 23 | }; 24 | 25 | /** Decoders should return n>0 for n packets successfully decoded, 26 | an ABORT code if the bitbuffer is no applicable, 27 | or a FAIL code if the message is malformed. */ 28 | enum decode_return_codes { 29 | DECODE_FAIL_OTHER = 0, ///< legacy, do not use 30 | /** Bitbuffer row count or row length is wrong for this sensor. */ 31 | DECODE_ABORT_LENGTH = -1, 32 | DECODE_ABORT_EARLY = -2, 33 | /** Message Integrity Check failed: e.g. checksum/CRC doesn't validate. */ 34 | DECODE_FAIL_MIC = -3, 35 | DECODE_FAIL_SANITY = -4, 36 | }; 37 | 38 | struct bitbuffer; 39 | struct data; 40 | 41 | /** Device protocol decoder struct. */ 42 | typedef struct r_device { 43 | unsigned protocol_num; ///< fixed sequence number, assigned in main(). 44 | 45 | /* information provided by each decoder */ 46 | char *name; 47 | unsigned modulation; 48 | float short_width; 49 | float long_width; 50 | float reset_limit; 51 | float gap_limit; 52 | float sync_width; 53 | float tolerance; 54 | int (*decode_fn)(struct r_device *decoder, struct bitbuffer *bitbuffer); 55 | struct r_device *(*create_fn)(char *args); 56 | unsigned priority; ///< Run later and only if no previous events were produced 57 | unsigned disabled; ///< 0: default enabled, 1: default disabled, 2: disabled, 3: disabled and hidden 58 | char **fields; ///< List of fields this decoder produces; required for CSV output. NULL-terminated. 59 | 60 | /* public for each decoder */ 61 | int verbose; 62 | int verbose_bits; 63 | void (*output_fn)(struct r_device *decoder, struct data *data); 64 | 65 | /* Decoder results / statistics */ 66 | unsigned decode_events; 67 | unsigned decode_ok; 68 | unsigned decode_messages; 69 | unsigned decode_fails[5]; 70 | 71 | /* private for flex decoder and output callback */ 72 | void *decode_ctx; 73 | void *output_ctx; 74 | } r_device; 75 | 76 | #endif /* INCLUDE_R_DEVICE_H_ */ 77 | -------------------------------------------------------------------------------- /include/r_api.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | Generic RF data receiver and decoder for ISM band devices using RTL-SDR and SoapySDR. 3 | 4 | Copyright (C) 2019 Christian W. Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #ifndef INCLUDE_R_API_H_ 13 | #define INCLUDE_R_API_H_ 14 | 15 | #include 16 | 17 | struct r_cfg; 18 | struct r_device; 19 | struct data; 20 | struct pulse_data; 21 | struct list; 22 | struct mg_mgr; 23 | 24 | /* general */ 25 | 26 | char const *version_string(void); 27 | 28 | struct r_cfg *r_create_cfg(void); 29 | 30 | void r_init_cfg(struct r_cfg *cfg); 31 | 32 | void r_free_cfg(struct r_cfg *cfg); 33 | 34 | /* device decoder protocols */ 35 | 36 | void register_protocol(struct r_cfg *cfg, struct r_device *r_dev, char *arg); 37 | 38 | void free_protocol(struct r_device *r_dev); 39 | 40 | void unregister_protocol(struct r_cfg *cfg, struct r_device *r_dev); 41 | 42 | void register_all_protocols(struct r_cfg *cfg, unsigned disabled); 43 | 44 | /* output helper */ 45 | 46 | void calc_rssi_snr(struct r_cfg *cfg, struct pulse_data *pulse_data); 47 | 48 | char *time_pos_str(struct r_cfg *cfg, unsigned samples_ago, char *buf); 49 | 50 | char const **well_known_output_fields(struct r_cfg *cfg); 51 | 52 | char const **determine_csv_fields(struct r_cfg *cfg, char const *const *well_known, int *num_fields); 53 | 54 | int run_ook_demods(struct list *r_devs, struct pulse_data *pulse_data); 55 | 56 | int run_fsk_demods(struct list *r_devs, struct pulse_data *fsk_pulse_data); 57 | 58 | /* handlers */ 59 | 60 | void event_occurred_handler(struct r_cfg *cfg, struct data *data); 61 | 62 | void data_acquired_handler(struct r_device *r_dev, struct data *data); 63 | 64 | struct data *create_report_data(struct r_cfg *cfg, int level); 65 | 66 | void flush_report_data(struct r_cfg *cfg); 67 | 68 | /* setup */ 69 | 70 | void add_json_output(struct r_cfg *cfg, char *param); 71 | 72 | void add_csv_output(struct r_cfg *cfg, char *param); 73 | 74 | void add_kv_output(struct r_cfg *cfg, char *param); 75 | 76 | void add_mqtt_output(struct r_cfg *cfg, char *param); 77 | 78 | void add_influx_output(struct r_cfg *cfg, char *param); 79 | 80 | void add_syslog_output(struct r_cfg *cfg, char *param); 81 | 82 | void add_http_output(struct r_cfg *cfg, char *param); 83 | 84 | void add_null_output(struct r_cfg *cfg, char *param); 85 | 86 | void start_outputs(struct r_cfg *cfg, char const *const *well_known); 87 | 88 | void add_sr_dumper(struct r_cfg *cfg, char const *spec, int overwrite); 89 | 90 | void close_dumpers(struct r_cfg *cfg); 91 | 92 | void add_dumper(struct r_cfg *cfg, char const *spec, int overwrite); 93 | 94 | void add_infile(struct r_cfg *cfg, char *in_file); 95 | 96 | void add_data_tag(struct r_cfg *cfg, char *param); 97 | 98 | /* runtime */ 99 | 100 | struct mg_mgr *get_mgr(struct r_cfg *cfg); 101 | 102 | void set_center_freq(struct r_cfg *cfg, uint32_t center_freq); 103 | 104 | void set_freq_correction(struct r_cfg *cfg, int freq_correction); 105 | 106 | void set_sample_rate(struct r_cfg *cfg, uint32_t sample_rate); 107 | 108 | void set_gain_str(struct r_cfg *cfg, char const *gain_str); 109 | 110 | #endif /* INCLUDE_R_API_H_ */ 111 | -------------------------------------------------------------------------------- /src/rtl_433/devices/eurochron.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Eurochron temperature and humidity sensor. 3 | 4 | Copyright (c) 2019 by Oliver Weyhmüller 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Eurochron temperature and humidity sensor. 13 | 14 | Datagram format: 15 | 16 | IIIIIIII B00P0000 HHHHHHHH TTTTTTTT TTTT 17 | 18 | - I: ID (new ID will be generated at battery change!) 19 | - B: Battery low 20 | - P: TX-Button pressed 21 | - H: Humidity (%) 22 | - T: Temperature (°C10) 23 | - 0: Unknown / always zero 24 | 25 | Device type identification is only possible by datagram length 26 | and some zero bits. Therefore this device is disabled 27 | by default (as it could easily trigger false alarms). 28 | 29 | Observed update intervals: 30 | - transmission time slot every 12 seconds 31 | - at least once within 120 seconds (with stable values) 32 | - down to 12 seconds (with rapidly changing values) 33 | */ 34 | 35 | #include "decoder.h" 36 | 37 | static int eurochron_decode(r_device *decoder, bitbuffer_t *bitbuffer) 38 | { 39 | data_t *data; 40 | int row; 41 | uint8_t *b; 42 | int temp_raw, humidity, device, battery_low, button; 43 | float temp_c; 44 | 45 | /* Validation checks */ 46 | row = bitbuffer_find_repeated_row(bitbuffer, 3, 36); 47 | 48 | if (row < 0) // repeated rows? 49 | return DECODE_ABORT_EARLY; 50 | 51 | if (bitbuffer->bits_per_row[row] > 36) // 36 bits per row? 52 | return DECODE_ABORT_LENGTH; 53 | 54 | b = bitbuffer->bb[row]; 55 | 56 | if (b[1] & 0x0F) // is lower nibble of second byte zero? 57 | return DECODE_FAIL_SANITY; 58 | 59 | /* Extract data */ 60 | device = b[0]; 61 | 62 | temp_raw = (int16_t)((b[3] << 8) | (b[4] & 0xf0)); 63 | temp_c = (temp_raw >> 4) * 0.1f; 64 | 65 | humidity = b[2]; 66 | 67 | battery_low = b[1] >> 7; 68 | 69 | button = (b[1] & 0x10) >> 4; 70 | 71 | /* clang-format off */ 72 | data = data_make( 73 | "model", "", DATA_STRING, "Eurochron-TH", 74 | "id", "", DATA_INT, device, 75 | "battery_ok", "Battery", DATA_INT, !battery_low, 76 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, 77 | "humidity", "Humidity", DATA_INT, humidity, 78 | "button", "Button", DATA_INT, button, 79 | NULL); 80 | /* clang-format on */ 81 | 82 | decoder_output_data(decoder, data); 83 | return 1; 84 | } 85 | 86 | static char *output_fields[] = { 87 | "model", 88 | "id", 89 | "battery_ok", 90 | "temperature_C", 91 | "humidity", 92 | "button", 93 | NULL, 94 | }; 95 | 96 | r_device eurochron = { 97 | .name = "Eurochron temperature and humidity sensor", 98 | .modulation = OOK_PULSE_PPM, 99 | .short_width = 1016, 100 | .long_width = 2024, 101 | .gap_limit = 2100, 102 | .reset_limit = 8200, 103 | .decode_fn = &eurochron_decode, 104 | .disabled = 1, 105 | .fields = output_fields, 106 | }; 107 | -------------------------------------------------------------------------------- /src/rtl_433/devices/inovalley-kw9015b.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Inovalley kw9015b rain and Temperature weather station. 3 | 4 | Copyright (C) 2015 Alexandre Coffignal 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Inovalley kw9015b rain and Temperature weather station. 13 | 14 | Also TFA-Dostmann rain-sensor 30.3161 (see #1531) with a 0.45mm rain per tip. 15 | 16 | Data layout: 17 | 18 | IIII??RR BRRPtttt TTTTTTTT rrrrrrrr CCCC 19 | 20 | - I : 4-bit ID 21 | - ? : 2-bit unknown always 00 22 | - T : 12-bit Temp in C, signed, scaled by 10 23 | - R : 12-bit Rain 24 | - B : 1-bit battery (0 means battery ok, 1 means low battery) 25 | - P : 1-bit power up (when batteries are inserted is 1, then always 0) 26 | - C : 4-bit Checksum (nibble sum) 27 | */ 28 | 29 | #include "decoder.h" 30 | 31 | static int kw9015b_callback(r_device *decoder, bitbuffer_t *bitbuffer) 32 | { 33 | data_t *data; 34 | int row; 35 | uint8_t *b; 36 | int temp_raw, rain, device; 37 | unsigned char chksum; 38 | float temp_c; 39 | 40 | row = bitbuffer_find_repeated_row(bitbuffer, 3, 36); 41 | if (row < 0) 42 | return DECODE_ABORT_EARLY; 43 | 44 | if (bitbuffer->bits_per_row[row] > 36) 45 | return DECODE_ABORT_LENGTH; 46 | 47 | b = bitbuffer->bb[row]; 48 | 49 | device = (reverse8(b[0]) & 0x0f); 50 | temp_raw = (int16_t)((reverse8(b[2]) << 8) | (reverse8(b[1]) & 0xf0)); // sign-extend 51 | temp_c = (temp_raw >> 4) * 0.1f; 52 | rain = ((reverse8(b[0]) & 0xc0) << 4) | ((reverse8(b[1]) & 0x06) << 7) | reverse8(b[3]); 53 | chksum = ((reverse8(b[0]) >> 4) + (reverse8(b[0]) & 0x0f) + 54 | (reverse8(b[1]) >> 4) + (reverse8(b[1]) & 0x0f) + 55 | (reverse8(b[2]) >> 4) + (reverse8(b[2]) & 0x0f) + 56 | (reverse8(b[3]) >> 4) + (reverse8(b[3]) & 0x0f)); 57 | int battery_low = b[1] >> 7; 58 | 59 | if ((chksum & 0x0f) != (reverse8(b[4]) & 0x0f)) 60 | return DECODE_FAIL_MIC; 61 | 62 | /* clang-format off */ 63 | data = data_make( 64 | "model", "", DATA_STRING, "Inovalley-kw9015b", 65 | "id", "", DATA_INT, device, 66 | "battery_ok", "Battery", DATA_INT, !battery_low, 67 | "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, 68 | "rain", "Rain Count", DATA_INT, rain, // TODO: remove this 69 | "rain_mm", "Rain Total", DATA_DOUBLE, rain * 0.45f, 70 | NULL); 71 | /* clang-format on */ 72 | 73 | decoder_output_data(decoder, data); 74 | return 1; 75 | } 76 | 77 | static char *kw9015b_csv_output_fields[] = { 78 | "model", 79 | "id", 80 | "battery_ok", 81 | "temperature_C", 82 | "rain", // TODO: remove this 83 | "rain_mm", 84 | NULL, 85 | }; 86 | 87 | r_device kw9015b = { 88 | .name = "Inovalley kw9015b, TFA Dostmann 30.3161 (Rain and temperature sensor)", 89 | .modulation = OOK_PULSE_PPM, 90 | .short_width = 2000, 91 | .long_width = 4000, 92 | .gap_limit = 4800, 93 | .reset_limit = 10000, 94 | .decode_fn = &kw9015b_callback, 95 | .disabled = 1, 96 | .fields = kw9015b_csv_output_fields, 97 | }; 98 | -------------------------------------------------------------------------------- /src/rtl_433/devices/tfa_pool_thermometer.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | TFA pool temperature sensor. 3 | 4 | Copyright (C) 2015 Alexandre Coffignal 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | TFA pool temperature sensor. 13 | 14 | 10 24 bits frames 15 | 16 | CCCCIIII IIIITTTT TTTTTTTT DDBF 17 | 18 | - C: checksum, sum of nibbles - 1 19 | - I: device id (changing only after reset) 20 | - T: temperature 21 | - D: channel number 22 | - B: battery status 23 | - F: first transmission 24 | */ 25 | 26 | #include "decoder.h" 27 | 28 | static int tfa_pool_thermometer_decode(r_device *decoder, bitbuffer_t *bitbuffer) 29 | { 30 | data_t *data; 31 | uint8_t *b; 32 | int checksum, checksum_rx, device, channel, battery; 33 | int temp_raw; 34 | float temp_f; 35 | 36 | // require 7 of 10 repeats 37 | int row = bitbuffer_find_repeated_row(bitbuffer, 7, 28); 38 | if (row < 0) { 39 | return DECODE_ABORT_EARLY; // no repeated row found 40 | } 41 | if (bitbuffer->bits_per_row[row] != 28) { 42 | return DECODE_ABORT_LENGTH; // prevent false positives 43 | } 44 | 45 | b = bitbuffer->bb[row]; 46 | 47 | checksum_rx = ((b[0] & 0xF0) >> 4); 48 | checksum = ((b[0] & 0x0F) + 49 | (b[1] >> 4) + 50 | (b[1] & 0x0F) + 51 | (b[2] >> 4) + 52 | (b[2] & 0x0F) + 53 | (b[3] >> 4) - 1); 54 | 55 | if (checksum_rx != (checksum & 0x0F)) { 56 | if (decoder->verbose > 1) 57 | bitrow_printf(b, bitbuffer->bits_per_row[row], "%s: checksum fail (%02x) ", __func__, checksum); 58 | return DECODE_FAIL_MIC; 59 | } 60 | 61 | device = ((b[0] & 0x0F) << 4) + ((b[1] & 0xF0) >> 4); 62 | temp_raw = ((b[1] & 0x0F) << 8) + b[2]; 63 | temp_f = (temp_raw > 2048 ? temp_raw - 4096 : temp_raw) * 0.1f; 64 | channel = ((b[3] & 0xC0) >> 6); 65 | battery = ((b[3] & 0x20) >> 5); 66 | 67 | /* clang-format off */ 68 | data = data_make( 69 | "model", "", DATA_STRING, "TFA-Pool", 70 | "id", "Id", DATA_INT, device, 71 | "channel", "Channel", DATA_INT, channel, 72 | "battery_ok", "Battery", DATA_INT, battery, 73 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_f, 74 | "mic", "Integrity", DATA_STRING, "CHECKSUM", 75 | NULL); 76 | /* clang-format on */ 77 | 78 | decoder_output_data(decoder, data); 79 | return 1; 80 | 81 | } 82 | 83 | static char *output_fields[] = { 84 | "model", 85 | "id", 86 | "channel", 87 | "battery_ok", 88 | "temperature_C", 89 | "mic", 90 | NULL, 91 | }; 92 | 93 | r_device tfa_pool_thermometer = { 94 | .name = "TFA pool temperature sensor", 95 | .modulation = OOK_PULSE_PPM, 96 | .short_width = 2000, 97 | .long_width = 4600, 98 | .gap_limit = 7800, 99 | .reset_limit = 10000, 100 | .decode_fn = &tfa_pool_thermometer_decode, 101 | .disabled = 0, 102 | .fields = output_fields, 103 | }; 104 | -------------------------------------------------------------------------------- /src/rtl_433/devices/waveman.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Example of a generic remote using PT2260/PT2262 SC2260/SC2262 EV1527 protocol. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | */ 10 | 11 | /** 12 | Example of a generic remote using PT2260/PT2262 SC2260/SC2262 EV1527 protocol. 13 | 14 | fixed bit width of 1445 us 15 | short pulse is 357 us (1/4th) 16 | long pulse is 1064 (3/4th) 17 | a packet is 15 pulses, the last pulse (short) is sync pulse 18 | packet gap is 11.5 ms 19 | 20 | note that this decoder uses: 21 | short-short (1 1 by the demod) as 0 (per protocol), 22 | short-long (1 0 by the demod) as 1 (F per protocol), 23 | long-long (0 0 by the demod) not used (1 per protocol). 24 | */ 25 | 26 | #include "decoder.h" 27 | 28 | static int waveman_callback(r_device *decoder, bitbuffer_t *bitbuffer) 29 | { 30 | data_t *data; 31 | uint8_t *b = bitbuffer->bb[0]; 32 | uint8_t nb[3] = {0}; // maps a pair of bits to two states, 1 0 -> 1 and 1 1 -> 0 33 | char id_str[2]; 34 | int i; 35 | 36 | /* TODO: iterate through all rows */ 37 | 38 | /* Reject codes of wrong length */ 39 | if (25 != bitbuffer->bits_per_row[0]) 40 | return DECODE_ABORT_LENGTH; 41 | 42 | /* 43 | * Catch the case triggering false positive for other transmitters. 44 | * example: Brennstuhl RCS 2044SN 45 | * TODO: is this message valid at all??? if not then put more validation below 46 | * instead of this special case 47 | */ 48 | if (0xFF == b[0] && 49 | 0xFF == b[1] && 50 | 0xFF == b[2]) 51 | return DECODE_ABORT_EARLY; 52 | 53 | /* Test if the bit stream has every even bit set to one */ 54 | if (((b[0] & 0xaa) != 0xaa) || ((b[1] & 0xaa) != 0xaa) || ((b[2] & 0xaa) != 0xaa)) 55 | return DECODE_FAIL_SANITY; 56 | 57 | /* Extract data from the bit stream */ 58 | for (i = 0; i < 3; ++i) { 59 | nb[i] = ((b[i] & 0x40) ? 0x00 : 0x01) 60 | | ((b[i] & 0x10) ? 0x00 : 0x02) 61 | | ((b[i] & 0x04) ? 0x00 : 0x04) 62 | | ((b[i] & 0x01) ? 0x00 : 0x08); 63 | } 64 | 65 | id_str[0] = 'A' + nb[0]; 66 | id_str[1] = '\0'; 67 | 68 | /* clang-format off */ 69 | data = data_make( 70 | "model", "", DATA_STRING, "Waveman-Switch", 71 | "id", "", DATA_STRING, id_str, 72 | "channel", "", DATA_INT, (nb[1] >> 2) + 1, 73 | "button", "", DATA_INT, (nb[1] & 3) + 1, 74 | "state", "", DATA_STRING, (nb[2] == 0xe) ? "on" : "off", 75 | NULL); 76 | /* clang-format on */ 77 | decoder_output_data(decoder, data); 78 | 79 | return 1; 80 | } 81 | 82 | static char *output_fields[] = { 83 | "model", 84 | "id", 85 | "channel", 86 | "button", 87 | "state", 88 | NULL 89 | }; 90 | 91 | r_device waveman = { 92 | .name = "Waveman Switch Transmitter", 93 | .modulation = OOK_PULSE_PWM, 94 | .short_width = 357, 95 | .long_width = 1064, 96 | .gap_limit = 1400, 97 | .reset_limit = 12000, 98 | .sync_width = 0, // No sync bit used 99 | .tolerance = 200, // us 100 | .decode_fn = &waveman_callback, 101 | .disabled = 0, 102 | .fields = output_fields 103 | }; 104 | -------------------------------------------------------------------------------- /src/rtl_433/devices/wt0124.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | WT0124 Pool Thermometer decoder. 3 | 4 | Copyright (C) 2018 Benjamin Larsson 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | /** 13 | WT0124 Pool Thermometer decoder. 14 | 15 | 5e ba 9a 9f e1 34 16 | 01011110 10111010 10011010 10011111 11100001 00110100 17 | 5555RRRR RRRRTTTT TTTTTTTT UUCCFFFF XXXXXXXX ???????? 18 | 19 | - 5 = constant 5 20 | - R = random power on id 21 | - T = 12 bits of temperature with 0x900 bias and scaled by 10 22 | - U = unk, maybe battery indicator (display is missing one though) 23 | - C = channel 24 | - F = constant F 25 | - X = xor checksum 26 | - ? = unknown 27 | 28 | */ 29 | 30 | #include "decoder.h" 31 | 32 | 33 | static int wt1024_callback(r_device *decoder, bitbuffer_t *bitbuffer) 34 | { 35 | data_t *data; 36 | uint8_t *b; // bits of a row 37 | uint16_t sensor_rid; 38 | int16_t value; 39 | float temp_c; 40 | uint8_t channel; 41 | 42 | if (bitbuffer->bits_per_row[1] !=49) 43 | return DECODE_ABORT_LENGTH; 44 | 45 | 46 | /* select row after preamble */ 47 | b = bitbuffer->bb[1]; 48 | 49 | /* Validate constant */ 50 | if (b[0]>>4 != 0x5) { 51 | return DECODE_ABORT_EARLY; 52 | } 53 | 54 | /* Validate checksum */ 55 | if ((b[0]^b[1]^b[2]^b[3]) != b[4]) 56 | return DECODE_FAIL_MIC; 57 | 58 | /* Get rid */ 59 | sensor_rid = (b[0]&0x0F)<<4 | (b[1]&0x0F); 60 | 61 | /* Get temperature */ 62 | temp_c = ((((b[1] & 0xF) << 8) | b[2]) - 0x990) * 0.1f; 63 | 64 | /* Get channel */ 65 | channel = ((b[3]>>4) & 0x3); 66 | 67 | /* unk */ 68 | value = b[5]; 69 | 70 | if (decoder->verbose) { 71 | fprintf(stderr, "wt1024_callback:"); 72 | bitbuffer_print(bitbuffer); 73 | } 74 | 75 | /* clang-format off */ 76 | data = data_make( 77 | "model", "", DATA_STRING, "WT0124-Pool", 78 | "id", "Random ID", DATA_INT, sensor_rid, 79 | "channel", "Channel", DATA_INT, channel, 80 | "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, 81 | "data", "Data", DATA_INT, value, 82 | "mic", "Integrity", DATA_STRING, "CHECKSUM", 83 | NULL); 84 | /* clang-format on */ 85 | 86 | decoder_output_data(decoder, data); 87 | 88 | // Return 1 if message successfully decoded 89 | return 1; 90 | } 91 | 92 | /* 93 | * List of fields that may appear in the output 94 | * 95 | * Used to determine what fields will be output in what 96 | * order for this device when using -F csv. 97 | * 98 | */ 99 | static char *output_fields[] = { 100 | "model", 101 | "id", 102 | "channel", 103 | "temperature_C", 104 | "data", 105 | "mic", 106 | NULL, 107 | }; 108 | 109 | r_device wt1024 = { 110 | .name = "WT0124 Pool Thermometer", 111 | .modulation = OOK_PULSE_PWM, 112 | .short_width = 680, 113 | .long_width = 1850, 114 | .reset_limit = 30000, 115 | .gap_limit = 4000, 116 | .sync_width = 10000, 117 | .decode_fn = &wt1024_callback, 118 | .disabled = 0, 119 | .fields = output_fields, 120 | }; 121 | -------------------------------------------------------------------------------- /src/rtl_433/devices/solight_te44.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Solight TE44 temperature sensor. 3 | 4 | Copyright (C) 2017 Miroslav Oujesky 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** @fn int solight_te44_callback(r_device *decoder, bitbuffer_t *bitbuffer) 12 | Solight TE44 -- Generic wireless thermometer, which might be sold as part of different kits. 13 | 14 | So far these were identified (mostly sold in central/eastern europe) 15 | - Solight TE44 16 | - Solight TE66 17 | - EMOS E0107T 18 | - NX-6876-917 from Pearl (for FWS-70 station). 19 | - newer TFA 30.3197 20 | 21 | Rated -50 C to 70 C, frequency 433,92 MHz, three selectable channels. 22 | 23 | Data structure: 24 | 25 | 12 repetitions of the same 36 bit payload, 1bit zero as a separator between each repetition. 26 | 27 | 36 bit payload format: iiiiiiii b0ccmmmm tttttttt 1111xxxx xxxx 28 | 29 | - i: 8 bit random key (changes after device reset) 30 | - b 1 bit battery flag: 1 if battery is ok, 0 if battery is low 31 | - c: 2 bit channel (0-2) 32 | - t: 12 bit temperature in celsius, signed integer, scale 10 33 | - x: 8 bit checksum 34 | 35 | */ 36 | 37 | #include "decoder.h" 38 | 39 | // NOTE: this should really not be here 40 | int rubicson_crc_check(uint8_t *b); 41 | 42 | static int solight_te44_callback(r_device *decoder, bitbuffer_t *bitbuffer) 43 | { 44 | data_t *data; 45 | uint8_t *b; 46 | int id, channel, temp_raw; 47 | // int battery; 48 | float temp_c; 49 | 50 | int r = bitbuffer_find_repeated_row(bitbuffer, 3, 36); 51 | if (r < 0) 52 | return DECODE_ABORT_EARLY; 53 | 54 | b = bitbuffer->bb[r]; 55 | 56 | if (bitbuffer->bits_per_row[r] != 37) 57 | return DECODE_ABORT_LENGTH; 58 | 59 | if ((b[3] & 0xf0) != 0xf0) 60 | return DECODE_ABORT_EARLY; // const not 1111 61 | 62 | if (!rubicson_crc_check(b)) 63 | return DECODE_ABORT_EARLY; 64 | 65 | id = b[0]; 66 | //battery = (b[1] & 0x80); 67 | channel = ((b[1] & 0x30) >> 4); 68 | temp_raw = (int16_t)((b[1] << 12) | (b[2] << 4)); // sign-extend 69 | temp_c = (temp_raw >> 4) * 0.1f; 70 | 71 | /* clang-format off */ 72 | data = data_make( 73 | "model", "", DATA_STRING, "Solight-TE44", 74 | "id", "Id", DATA_INT, id, 75 | "channel", "Channel", DATA_INT, channel + 1, 76 | // "battery_ok", "Battery", DATA_INT, !!battery, 77 | "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c, 78 | "mic", "Integrity", DATA_STRING, "CRC", 79 | NULL); 80 | /* clang-format on */ 81 | 82 | decoder_output_data(decoder, data); 83 | return 1; 84 | } 85 | 86 | static char *output_fields[] = { 87 | "model", 88 | "id", 89 | "channel", 90 | //"battery_ok", 91 | "temperature_C", 92 | "mic", 93 | NULL, 94 | }; 95 | 96 | r_device solight_te44 = { 97 | .name = "Solight TE44/TE66, EMOS E0107T, NX-6876-917", 98 | .modulation = OOK_PULSE_PPM, 99 | .short_width = 972, // short gap = 972 us 100 | .long_width = 1932, // long gap = 1932 us 101 | .gap_limit = 3000, // packet gap = 3880 us 102 | .reset_limit = 6000, 103 | .decode_fn = &solight_te44_callback, 104 | .fields = output_fields, 105 | }; 106 | -------------------------------------------------------------------------------- /src/rtl_433/devices/chuango.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Chuango Security Technology. 3 | 4 | Copyright (C) 2015 Tommy Vestermark 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | */ 12 | /** 13 | Chuango Security Technology. 14 | 15 | likely based on HS1527 or compatible 16 | 17 | Tested devices: 18 | G5 GSM/SMS/RFID Touch Alarm System (Alarm, Disarm, ...) 19 | DWC-100 Door sensor (Default: Normal Zone) 20 | DWC-102 Door sensor (Default: Normal Zone) 21 | KP-700 Wireless Keypad (Arm, Disarm, Home Mode, Alarm!) 22 | PIR-900 PIR sensor (Default: Home Mode Zone) 23 | RC-80 Remote Control (Arm, Disarm, Home Mode, Alarm!) 24 | SMK-500 Smoke sensor (Default: 24H Zone) 25 | WI-200 Water sensor (Default: 24H Zone) 26 | 27 | Note: simple 24 bit fixed ID protocol (x1527 style) and should be handled by the flex decoder. 28 | 29 | */ 30 | 31 | 32 | #include "decoder.h" 33 | 34 | static int chuango_callback(r_device *decoder, bitbuffer_t *bitbuffer) 35 | { 36 | data_t *data; 37 | uint8_t *b; 38 | int id; 39 | int cmd; 40 | char *cmd_str; 41 | 42 | if (bitbuffer->bits_per_row[0] != 25) 43 | return DECODE_ABORT_LENGTH; 44 | b = bitbuffer->bb[0]; 45 | 46 | b[0] = ~b[0]; 47 | b[1] = ~b[1]; 48 | b[2] = ~b[2]; 49 | 50 | // Validate package 51 | if (!(b[3] & 0x80) // Last bit (MSB here) is always 1 52 | || !b[0] || !b[1] || !(b[2] & 0xF0)) // Reduce false positives. ID 0x00000 not supported 53 | return DECODE_ABORT_EARLY; 54 | 55 | id = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); // ID is 20 bits (Ad: "1 Million combinations" :-) 56 | cmd = b[2] & 0x0F; 57 | 58 | switch(cmd) { 59 | case 0xF: cmd_str = "?"; break; 60 | case 0xE: cmd_str = "?"; break; 61 | case 0xD: cmd_str = "Low Battery"; break; 62 | case 0xC: cmd_str = "?"; break; 63 | case 0xB: cmd_str = "24H Zone"; break; 64 | case 0xA: cmd_str = "Single Delay Zone"; break; 65 | case 0x9: cmd_str = "?"; break; 66 | case 0x8: cmd_str = "Arm"; break; 67 | case 0x7: cmd_str = "Normal Zone"; break; 68 | case 0x6: cmd_str = "Home Mode Zone"; break; 69 | case 0x5: cmd_str = "On"; break; 70 | case 0x4: cmd_str = "Home Mode"; break; 71 | case 0x3: cmd_str = "Tamper"; break; 72 | case 0x2: cmd_str = "Alarm"; break; 73 | case 0x1: cmd_str = "Disarm"; break; 74 | case 0x0: cmd_str = "Test"; break; 75 | default: cmd_str = ""; break; 76 | } 77 | 78 | data = data_make( 79 | "model", "", DATA_STRING, "Chuango-Security", 80 | "id", "ID", DATA_INT, id, 81 | "cmd", "CMD", DATA_STRING, cmd_str, 82 | "cmd_id", "CMD_ID", DATA_INT, cmd, 83 | NULL); 84 | 85 | decoder_output_data(decoder, data); 86 | return 1; 87 | } 88 | 89 | static char *output_fields[] = { 90 | "model", 91 | "id", 92 | "cmd", 93 | "cmd_id", 94 | NULL, 95 | }; 96 | 97 | r_device chuango = { 98 | .name = "Chuango Security Technology", 99 | .modulation = OOK_PULSE_PWM, 100 | .short_width = 568, // Pulse: Short 568µs, Long 1704µs 101 | .long_width = 1704, // Gaps: Short 568µs, Long 1696µs 102 | .reset_limit = 1800, // Intermessage Gap 17200µs (individually for now) 103 | .sync_width = 0, // No sync bit used 104 | .tolerance = 160, // us 105 | .decode_fn = &chuango_callback, 106 | .disabled = 0, 107 | .fields = output_fields, 108 | }; 109 | -------------------------------------------------------------------------------- /src/rtl_433/devices/wg_pb12v1.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | WG-PB12V1 Temperature Sensor. 3 | 4 | Copyright (C) 2015 Tommy Vestermark 5 | Modifications Copyright (C) 2017 Ciarán Mooney 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | */ 12 | /** 13 | WG-PB12V1 Temperature Sensor. 14 | 15 | Device method to decode a generic wireless temperature probe. Probe marked 16 | with WG-PB12V1-2016/11. 17 | 18 | Format of Packets: 19 | 20 | The packet format appears to be similar those the Lacrosse format. 21 | (http://fredboboss.free.fr/articles/tx29.php) 22 | 23 | AAAAAAAA MMMMTTTT TTTTTTTT ???IIIII HHHHHHHH CCCCCCCC 24 | 25 | - A: Preamble - 11111111 26 | - M: Message type?, fixed 0x3, e.g. Fine Offset WH2 has 0x4 here 27 | - T: Temperature, scale 10, offset 40 28 | - I: ID of probe is set randomly each time the device is powered off-on, 29 | Note, base station has and unused "123" symbol, but ID values can be 30 | higher than this. 31 | - H: Humidity - not used, is always 11111111 32 | - C: Checksum - CRC8, polynomial 0x31, initial value 0x0, final value 0x0 33 | 34 | Temperature: 35 | 36 | Temperature value is "deci-celsius", ie 10 dC = 1C, offset by -40 C. 37 | 38 | 0010 01011101 = 605 dC => 60.5 C 39 | Remove offset => 60.5 C - 40 C = 20.5 C 40 | 41 | Unknown: 42 | 43 | Possible uses could be weak battery, or new battery. 44 | 45 | At the moment it this device cannot distinguish between a Fine Offset 46 | device, see fineoffset.c. 47 | */ 48 | 49 | #include "decoder.h" 50 | 51 | static int wg_pb12v1_decode(r_device *decoder, bitbuffer_t *bitbuffer) 52 | { 53 | data_t *data; 54 | uint8_t *b; 55 | int id; 56 | int temp_raw; 57 | float temp_c; 58 | 59 | // Validate package 60 | b = bitbuffer->bb[0]; 61 | if (bitbuffer->bits_per_row[0] < 48) 62 | return DECODE_ABORT_LENGTH; 63 | if (b[0] != 0xFF) // Preamble 64 | return DECODE_ABORT_EARLY; 65 | if ((b[1] & 0xf0) != 0x30) // Message type, TODO: is this always a fixed value? 66 | return DECODE_ABORT_EARLY; 67 | if (b[5] != crc8(&b[1], 4, 0x31, 0)) // CRC (excluding preamble) 68 | return DECODE_FAIL_MIC; 69 | if (b[4] != 0xFF) // Humidity set to 11111111 70 | return DECODE_FAIL_OTHER; 71 | 72 | // Nibble 7,8 contains id 73 | id = b[3] & 0x1F; 74 | 75 | // Nibble 5,6,7 contains 12 bits of temperature 76 | // Temperature, scaled by 10, offset by -40 C. 77 | temp_raw = ((b[1] & 0x0F) << 8) | b[2]; 78 | temp_c = ((float)temp_raw * 0.1) - 40; 79 | 80 | /* clang-format off */ 81 | data = data_make( 82 | "model", "", DATA_STRING, "WG-PB12V1", 83 | "id", "ID", DATA_INT, id, 84 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, 85 | "mic", "Integrity", DATA_STRING, "CRC", 86 | NULL); 87 | /* clang-format on */ 88 | decoder_output_data(decoder, data); 89 | return 1; 90 | } 91 | 92 | static char *output_fields[] = { 93 | "model", 94 | "id", 95 | "temperature_C", 96 | "mic", 97 | NULL, 98 | }; 99 | 100 | r_device wg_pb12v1 = { 101 | .name = "WG-PB12V1 Temperature Sensor", 102 | .modulation = OOK_PULSE_PWM, 103 | .short_width = 564, // Short pulse 564µs, long pulse 1476µs, fixed gap 960µs 104 | .long_width = 1476, // Maximum pulse period (long pulse + fixed gap) 105 | .reset_limit = 2500, // We just want 1 package 106 | .decode_fn = &wg_pb12v1_decode, 107 | .disabled = 0, 108 | .fields = output_fields, 109 | }; 110 | -------------------------------------------------------------------------------- /src/rtl_433/devices/opus_xt300.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Opus/Imagintronix XT300 Soil Moisture Sensor. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | */ 10 | /** 11 | Opus/Imagintronix XT300 Soil Moisture Sensor. 12 | 13 | Also called XH300 sometimes, this seems to be the associated display name 14 | 15 | https://www.plantcaretools.com/product/wireless-moisture-monitor/ 16 | 17 | Data is transmitted with 6 bytes row: 18 | 19 | 0. 1. 2. 3. 4. 5 20 | FF ID SM TT ?? CC 21 | 22 | - FF: initial preamble 23 | - ID: 0101 01ID 24 | - SM: soil moisure (decimal 05 -> 99 %) 25 | - TT: temperature °C + 40°C (decimal) 26 | - ??: always FF... maybe spare bytes 27 | - CC: check sum (simple sum) except 0xFF preamble 28 | 29 | */ 30 | 31 | #include "decoder.h" 32 | 33 | static int opus_xt300_decode(r_device *decoder, bitbuffer_t *bitbuffer) 34 | { 35 | int ret = 0; 36 | int fail_code = 0; 37 | int row; 38 | int chk; 39 | uint8_t *b; 40 | int channel, temp, moisture; 41 | data_t *data; 42 | 43 | for (row = 0; row < bitbuffer->num_rows; row++) { 44 | 45 | if (bitbuffer->bits_per_row[row] != 48) { 46 | fail_code = DECODE_ABORT_LENGTH; 47 | continue; 48 | } 49 | 50 | b = bitbuffer->bb[row]; 51 | 52 | if (!b[0] && !b[1] && !b[2] && !b[3]) { 53 | if (decoder->verbose > 1) { 54 | fprintf(stderr, "%s: DECODE_FAIL_SANITY data all 0x00\n", __func__); 55 | } 56 | fail_code = DECODE_FAIL_SANITY; 57 | continue; 58 | } 59 | 60 | if (b[0] != 0xFF && ((b[1] | 0x1) & 0xFD) == 0x55) { 61 | fail_code = DECODE_ABORT_EARLY; 62 | continue; 63 | } 64 | chk = add_bytes(b + 1, 4); // sum bytes 1-4 65 | chk = chk & 0xFF; 66 | if (chk != 0 && chk != b[5] ) { 67 | fail_code = DECODE_FAIL_MIC; 68 | continue; 69 | } 70 | 71 | channel = (b[1] & 0x03); 72 | temp = b[3] - 40; 73 | moisture = b[2]; 74 | 75 | // unverified sales advert say Outdoor temperature range: -40°C to +65°C 76 | // test for Boiling water 77 | // over 100% soil humidity ? 78 | if (temp > 100 || moisture > 101) { 79 | // fprintf(stderr, "%s: temp %d moisture %d\n", __func__, temp, moisture); 80 | fail_code = DECODE_FAIL_SANITY; 81 | continue; 82 | } 83 | 84 | /* clang-format off */ 85 | data = data_make( 86 | "model", "", DATA_STRING, "Opus-XT300", 87 | "channel", "Channel", DATA_INT, channel, 88 | "temperature_C", "Temperature", DATA_FORMAT, "%.0f C", DATA_DOUBLE, (double)temp, 89 | "moisture", "Moisture", DATA_FORMAT, "%d %%", DATA_INT, moisture, 90 | "mic", "Integrity", DATA_STRING, "CHECKSUM", 91 | NULL); 92 | /* clang-format on */ 93 | 94 | decoder_output_data(decoder, data); 95 | ret++; 96 | } 97 | return ret > 0 ? ret : fail_code; 98 | } 99 | 100 | static char *output_fields[] = { 101 | "model", 102 | "channel", 103 | "temperature_C", 104 | "moisture", 105 | "mic", 106 | NULL, 107 | }; 108 | 109 | r_device opus_xt300 = { 110 | .name = "Opus/Imagintronix XT300 Soil Moisture", 111 | .modulation = OOK_PULSE_PWM, 112 | .short_width = 544, 113 | .long_width = 932, 114 | .gap_limit = 10000, 115 | .reset_limit = 31000, 116 | .decode_fn = &opus_xt300_decode, 117 | .fields = output_fields, 118 | }; 119 | -------------------------------------------------------------------------------- /src/rtl_433/devices/ht680.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | HT680 based Remote control (broadly similar to x1527 protocol). 3 | 4 | Copyright (C) 2016 Igor Polovnikov 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | HT680 based Remote control (broadly similar to x1527 protocol). 13 | 14 | - short is 850 us gap 260 us pulse 15 | - long is 434 us gap 663 us pulse 16 | 17 | */ 18 | 19 | #include "decoder.h" 20 | 21 | static int ht680_callback(r_device *decoder, bitbuffer_t *bitbuffer) 22 | { 23 | data_t *data; 24 | uint8_t b[5]; // 36 bits 25 | 26 | for (int row = 0; row < bitbuffer->num_rows; row++) { 27 | if (bitbuffer->bits_per_row[row] != 41 || // Length of packet is 41 (36+5) 28 | (bitbuffer->bb[row][0] & 0xf8) != 0xa8) // Sync is 10101xxx (5 bits) 29 | continue; // DECODE_ABORT_LENGTH 30 | 31 | // remove the 5 sync bits 32 | bitbuffer_extract_bytes(bitbuffer, row, 5, b, 36); 33 | 34 | if ((b[1] & 0xf0) != 0xa0 && // A4, A5 always "open" on HT680 35 | (b[2] & 0x0c) != 0x08 && // AD10 always "open" on HT680 36 | (b[3] & 0x30) != 0x20 && // AD13 always "open" on HT680 37 | (b[4] & 0xf0) != 0xa0) // AD16, AD17 always "open" on HT680 38 | continue; // DECODE_ABORT_EARLY 39 | 40 | // Tristate coding 41 | char tristate[21]; 42 | char *p = tristate; 43 | for (int byte = 0; byte < 5; byte++) { 44 | for (int bit = 7; bit > 0; bit -= 2) { 45 | switch ((b[byte] >> (bit-1)) & 0x03) { 46 | case 0x00: *p++ = '0'; break; 47 | case 0x01: *p++ = 'X'; break; // Invalid code 01 48 | case 0x02: *p++ = 'Z'; break; // Floating state Z is 10 49 | case 0x03: *p++ = '1'; break; 50 | default: *p++ = '?'; break; // Unknown error 51 | } 52 | } 53 | } 54 | // remove last two unused bits 55 | p -= 2; 56 | *p = '\0'; 57 | 58 | int address = (b[0]<<12) | (b[1]<<4) | b[2] >> 4; 59 | int button1 = (b[3]>>0) & 0x03; 60 | int button2 = (b[3]>>2) & 0x03; 61 | int button3 = (b[3]>>6) & 0x03; 62 | int button4 = (b[2]>>0) & 0x03; 63 | 64 | /* clang-format off */ 65 | data = data_make( 66 | "model", "", DATA_STRING, "HT680-Remote", 67 | "id", "Address", DATA_FORMAT, "0x%06X", DATA_INT, address, 68 | "button1", "Button 1", DATA_STRING, button1 == 3 ? "PRESSED" : "", 69 | "button2", "Button 2", DATA_STRING, button2 == 3 ? "PRESSED" : "", 70 | "button3", "Button 3", DATA_STRING, button3 == 3 ? "PRESSED" : "", 71 | "button4", "Button 4", DATA_STRING, button4 == 3 ? "PRESSED" : "", 72 | "tristate", "Tristate code", DATA_STRING, tristate, 73 | NULL); 74 | /* clang-format on */ 75 | 76 | decoder_output_data(decoder, data); 77 | return 1; 78 | } 79 | return 0; 80 | } 81 | 82 | static char *output_fields[] = { 83 | "model", 84 | "id", 85 | "button1", 86 | "button2", 87 | "button3", 88 | "button4", 89 | "tristate", 90 | NULL, 91 | }; 92 | 93 | r_device ht680 = { 94 | .name = "HT680 Remote control", 95 | .modulation = OOK_PULSE_PWM, 96 | .short_width = 200, 97 | .long_width = 600, 98 | .gap_limit = 1200, 99 | .reset_limit = 14000, 100 | .decode_fn = &ht680_callback, 101 | .disabled = 0, 102 | .fields = output_fields, 103 | }; 104 | -------------------------------------------------------------------------------- /src/rtl_433/devices/rubicson.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Rubicson temperature sensor. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | */ 10 | /** @fn int rubicson_callback(r_device *decoder, bitbuffer_t *bitbuffer) 11 | Rubicson temperature sensor. 12 | 13 | Also older TFA 30.3197 sensors. 14 | 15 | The sensor sends 12 packets of 36 bits pwm modulated data. 16 | 17 | data is grouped into 9 nibbles 18 | 19 | [id0] [id1] [bat|unk1|chan1|chan2] [temp0] [temp1] [temp2] [0xf] [crc1] [crc2] 20 | 21 | - The id changes when the battery is changed in the sensor. 22 | - bat bit is 1 if battery is ok, 0 if battery is low 23 | - unk1 is always 0 probably unused 24 | - chan1 and chan2 forms a 2bit value for the used channel 25 | - temp is 12 bit signed scaled by 10 26 | - F is always 0xf 27 | - crc1 and crc2 forms a 8-bit crc, polynomial 0x31, initial value 0x6c, final value 0x0 28 | 29 | The sensor can be bought at Kjell&Co 30 | */ 31 | 32 | #include "decoder.h" 33 | 34 | // NOTE: this is used in nexus.c and solight_te44.c 35 | int rubicson_crc_check(uint8_t *b); 36 | 37 | int rubicson_crc_check(uint8_t *b) 38 | { 39 | uint8_t tmp[5]; 40 | tmp[0] = b[0]; // Byte 0 is nibble 0 and 1 41 | tmp[1] = b[1]; // Byte 1 is nibble 2 and 3 42 | tmp[2] = b[2]; // Byte 2 is nibble 4 and 5 43 | tmp[3] = b[3]&0xf0; // Byte 3 is nibble 6 and 0-padding 44 | tmp[4] = (b[3]&0x0f)<<4 | // CRC is nibble 7 and 8 45 | (b[4]&0xf0)>>4; 46 | 47 | return crc8(tmp, 5, 0x31, 0x6c) == 0; 48 | } 49 | 50 | static int rubicson_callback(r_device *decoder, bitbuffer_t *bitbuffer) 51 | { 52 | data_t *data; 53 | uint8_t *b; 54 | int id, battery, channel, temp_raw; 55 | float temp_c; 56 | 57 | int r = bitbuffer_find_repeated_row(bitbuffer, 3, 36); 58 | if (r < 0) 59 | return DECODE_ABORT_EARLY; 60 | 61 | b = bitbuffer->bb[r]; 62 | 63 | if (bitbuffer->bits_per_row[r] != 36) 64 | return DECODE_ABORT_LENGTH; 65 | 66 | if ((b[3] & 0xf0) != 0xf0) 67 | return DECODE_ABORT_EARLY; // const not 1111 68 | 69 | if (!rubicson_crc_check(b)) 70 | return DECODE_FAIL_MIC; 71 | 72 | id = b[0]; 73 | battery = (b[1] & 0x80); 74 | channel = ((b[1] & 0x30) >> 4) + 1; 75 | temp_raw = (int16_t)((b[1] << 12) | (b[2] << 4)); // sign-extend 76 | temp_c = (temp_raw >> 4) * 0.1f; 77 | 78 | /* clang-format off */ 79 | data = data_make( 80 | "model", "", DATA_STRING, "Rubicson-Temperature", 81 | "id", "House Code", DATA_INT, id, 82 | "channel", "Channel", DATA_INT, channel, 83 | "battery_ok", "Battery", DATA_INT, !!battery, 84 | "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, 85 | "mic", "Integrity", DATA_STRING, "CRC", 86 | NULL); 87 | /* clang-format on */ 88 | 89 | decoder_output_data(decoder, data); 90 | return 1; 91 | } 92 | 93 | static char *output_fields[] = { 94 | "model", 95 | "id", 96 | "channel", 97 | "battery_ok", 98 | "temperature_C", 99 | "mic", 100 | NULL, 101 | }; 102 | 103 | // timings based on samp_rate=1024000 104 | r_device rubicson = { 105 | .name = "Rubicson Temperature Sensor", 106 | .modulation = OOK_PULSE_PPM, 107 | .short_width = 1000, // Gaps: Short 976us, Long 1940us, Sync 4000us 108 | .long_width = 2000, // Pulse: 500us (Initial pulse in each package is 388us) 109 | .gap_limit = 3000, 110 | .reset_limit = 4800, // Two initial pulses and a gap of 9120us is filtered out 111 | .decode_fn = &rubicson_callback, 112 | .fields = output_fields, 113 | }; 114 | -------------------------------------------------------------------------------- /src/rtl_433/devices/auriol_ahfl.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Auriol AHFL 433B2 IPX4 3 | 4 | Copyright (C) 2021 Benjamin Larsson 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | /** 13 | Lidl Auriol Auriol AHFL 433B2 IPX4 14 | 15 | [00] {42} f2 00 ef 7c 41 40 : 11110010 00000000 11101111 01111100 01000001 01 16 | 17 | II [BXCC]T TT HH FS [SS--] 18 | 19 | 42 bit message 20 | 21 | I - id, 8 bits 22 | B - batter, 1 bit 23 | X - tx-button, 1 bit (might not work) 24 | C - channel, 2 bits 25 | T - temperature, 12 bits 26 | H - humidity, 7 bits data, 1 bit 0 27 | F - always 0x4 (0100) 28 | S - nibble sum, 6 bits 29 | */ 30 | 31 | #include "decoder.h" 32 | 33 | static int auriol_ahfl_decode(r_device *decoder, bitbuffer_t *bitbuffer) 34 | { 35 | data_t *data; 36 | uint8_t *b; 37 | int row; 38 | int id; 39 | int channel; 40 | int tx_button; 41 | int battery_ok; 42 | int temp_raw; 43 | float temp_c; 44 | int humidity; 45 | int nibble_sum, checksum; 46 | 47 | row = bitbuffer_find_repeated_row(bitbuffer, 2, 42); 48 | if (row < 0) { 49 | return DECODE_ABORT_EARLY; // no repeated row found 50 | } 51 | 52 | if (bitbuffer->bits_per_row[row] != 42) 53 | return DECODE_ABORT_LENGTH; 54 | 55 | b = bitbuffer->bb[row]; 56 | 57 | /* Check fixed message values */ 58 | if (((b[4]&0xF0) != 0x40) || ((b[3]&0x1) != 0x0)) { 59 | return DECODE_FAIL_SANITY; 60 | } 61 | 62 | // calculate nibble sum 63 | nibble_sum = (b[0]&0xF) + (b[0]>>4) + 64 | (b[1]&0xF) + (b[1]>>4) + 65 | (b[2]&0xF) + (b[2]>>4) + 66 | (b[3]&0xF) + (b[3]>>4) + 67 | (b[4]>>4); 68 | checksum = ((b[4]&0xF) << 2) | ((b[5]&0xC0)>>6); 69 | 70 | // check 6 bits of nibble sum 71 | if ((nibble_sum&0x3F) != checksum) 72 | return DECODE_FAIL_MIC; 73 | 74 | id = b[0]; 75 | battery_ok = b[1] >> 7; 76 | channel = (b[1] & 0x30) >> 4; 77 | tx_button = (b[1] & 0x40) >> 6; 78 | temp_raw = (int16_t)(((b[1] & 0x0f) << 12) | (b[2] << 4)); // uses sign extend 79 | temp_c = (temp_raw >> 4) * 0.1f; 80 | humidity = b[3] >> 1; 81 | 82 | /* clang-format off */ 83 | data = data_make( 84 | "model", "", DATA_STRING, "Auriol-AHFL", 85 | "id", "", DATA_INT, id, 86 | "channel", "Channel", DATA_INT, channel + 1, 87 | "battery_ok", "Battery", DATA_INT, battery_ok, 88 | "button", "Button", DATA_INT, tx_button, 89 | "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, 90 | "humidity", "Humidity", DATA_FORMAT, "%d %%", DATA_INT, humidity, 91 | "mic", "Integrity", DATA_STRING, "CHECKSUM", 92 | NULL); 93 | /* clang-format on */ 94 | 95 | decoder_output_data(decoder, data); 96 | return 1; 97 | } 98 | 99 | static char *output_fields[] = { 100 | "model", 101 | "id", 102 | "channel", 103 | "battery_ok", 104 | "button", 105 | "temperature_C", 106 | "humidity", 107 | "mic", 108 | NULL, 109 | }; 110 | 111 | r_device auriol_ahfl = { 112 | .name = "Auriol AHFL temperature/humidity sensor", 113 | .modulation = OOK_PULSE_PPM, 114 | .short_width = 2100, 115 | .long_width = 4150, 116 | .sync_width = 0, // No sync bit used 117 | .gap_limit = 4248, 118 | .reset_limit = 9150, 119 | .decode_fn = &auriol_ahfl_decode, 120 | .disabled = 0, // No side effects known. 121 | .fields = output_fields, 122 | }; 123 | -------------------------------------------------------------------------------- /include/term_ctl.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | Terminal control utility functions. 3 | 4 | Copyright (C) 2018 Christian Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #ifndef INCLUDE_TERM_CTL_H_ 13 | #define INCLUDE_TERM_CTL_H_ 14 | 15 | #include 16 | 17 | void *term_init(FILE *fp); 18 | 19 | void term_free(void *ctx); 20 | 21 | int term_get_columns(void *ctx); 22 | 23 | int term_has_color(void *ctx); 24 | 25 | void term_ring_bell(void *ctx); 26 | 27 | typedef enum term_color { 28 | TERM_COLOR_RESET = 0, 29 | TERM_COLOR_BLACK = 30, 30 | TERM_COLOR_RED = 31, 31 | TERM_COLOR_GREEN = 32, 32 | TERM_COLOR_YELLOW = 33, 33 | TERM_COLOR_BLUE = 34, 34 | TERM_COLOR_MAGENTA = 35, 35 | TERM_COLOR_CYAN = 36, 36 | TERM_COLOR_WHITE = 37, 37 | TERM_COLOR_BRIGHT_BLACK = 90, 38 | TERM_COLOR_BRIGHT_RED = 91, 39 | TERM_COLOR_BRIGHT_GREEN = 92, 40 | TERM_COLOR_BRIGHT_YELLOW = 93, 41 | TERM_COLOR_BRIGHT_BLUE = 94, 42 | TERM_COLOR_BRIGHT_MAGENTA = 95, 43 | TERM_COLOR_BRIGHT_CYAN = 96, 44 | TERM_COLOR_BRIGHT_WHITE = 97, 45 | } term_color_t; 46 | 47 | void term_set_fg(void *ctx, term_color_t color); 48 | 49 | void term_set_bg(void *ctx, term_color_t color); 50 | 51 | /* 52 | * Defined in newer for MSVC. 53 | */ 54 | #ifndef _Printf_format_string_ 55 | #define _Printf_format_string_ 56 | #endif 57 | 58 | /** 59 | * Print to terminal with color-codes inline turned into above colors. 60 | * Takes a var-arg format. 61 | * 62 | * E.g.: 63 | * 64 | * void *term = term_init(stdout); 65 | * term_printf (term, "~4Hello ~2world~0.\n"); 66 | * 67 | * will print to stdout with 'Hello' mapped to colour 4 68 | * and 'world' mapped to colour 2. See 'term_set_color_map()' below. 69 | * 70 | * And a 'term_printf (NULL, "~4Hello ~2world~0.\n");' 71 | * will print "Hello world" to stderr' with no colors. 72 | */ 73 | int term_printf(void *ctx, _Printf_format_string_ const char *format, ...) 74 | #if defined(__GNUC__) || defined(__clang__) 75 | __attribute__((format(printf, 2, 3))) 76 | #endif 77 | ; 78 | 79 | /** 80 | * Print to terminal with markup turned into colors. 81 | * Like 'term_printf()', but with automatic coloring for markup. 82 | * 83 | * Markup: 84 | * = Heading = 85 | * [option argument] 86 | * "quoted" 87 | * 'quoted' 88 | */ 89 | int term_help_printf(_Printf_format_string_ char const *format, ...) 90 | #if defined(__GNUC__) || defined(__clang__) 91 | __attribute__((format(printf, 1, 2))) 92 | #endif 93 | ; 94 | 95 | /** 96 | * Like 'term_printf()', but no var-arg format. 97 | * Simply takes a 0-terminated buffer. 98 | */ 99 | int term_puts(void *ctx, const char *buf); 100 | 101 | /** 102 | * Like 'term_help_printf()', but no var-arg format. 103 | * Simply takes a 0-terminated buffer. 104 | */ 105 | int term_help_puts(void *ctx, const char *buf); 106 | 107 | /** 108 | * Change the default color map. 109 | * By default, the color-codes maps to these foreground colour: 110 | * "~0": always restores terminal-colors; TERM_COLOR_RESET. 111 | * "~1": print using TERM_COLOR_GREEN. 112 | * "~2": print using TERM_COLOR_WHITE. 113 | * "~3": print using TERM_COLOR_BLUE. 114 | * "~4": print using TERM_COLOR_CYAN. 115 | * "~5": print using TERM_COLOR_MAGENTA. 116 | * "~6": print using TERM_COLOR_YELLOW. 117 | * "~7": print using TERM_COLOR_BLACK. 118 | * "~8": print using TERM_COLOR_RED. 119 | */ 120 | int term_set_color_map(int idx, term_color_t color); 121 | 122 | /** 123 | * Returns the current color-value ('enum term_color') for color-index. 124 | * 'idx'. This index goes from ASCII '0' to 'X'. 125 | * 'X' = '0' + the dimension of the internal 'color_map[]'. 126 | */ 127 | int term_get_color_map(int idx); 128 | 129 | #endif /* INCLUDE_TERM_CTL_H_ */ 130 | -------------------------------------------------------------------------------- /example/FSK_Transmitter/README.md: -------------------------------------------------------------------------------- 1 | Sample program to create a FSK signal to test decoder logic and settings. Based on WH51 Soil Moisture Sensor Signal 2 | 3 | Original WH51 signal via rtl_433 4 | 5 | time : 2021-04-06 13:21:09 6 | model : Fineoffset-WH51 ID : 00ca1d 7 | Battery level: 1.111 Battery : 1700 mV Moisture : 0 % Transmission boost: 0 AD raw : 20 Integrity : CRC 8 | Analyzing pulses... 9 | Total count: 48, width: 10.26 ms ( 2564 S) 10 | Pulse width distribution: 11 | [ 0] count: 1, width: 0 us [0;0] ( 0 S) 12 | [ 1] count: 38, width: 56 us [56;68] ( 14 S) 13 | [ 2] count: 3, width: 116 us [116;120] ( 29 S) 14 | [ 3] count: 3, width: 172 us [172;176] ( 43 S) 15 | [ 4] count: 1, width: 404 us [404;404] ( 101 S) 16 | [ 5] count: 1, width: 288 us [288;288] ( 72 S) 17 | [ 6] count: 1, width: 1392 us [1392;1392] ( 348 S) 18 | Gap width distribution: 19 | [ 0] count: 33, width: 56 us [48;60] ( 14 S) 20 | [ 1] count: 6, width: 172 us [172;176] ( 43 S) 21 | [ 2] count: 2, width: 464 us [464;468] ( 116 S) 22 | [ 3] count: 3, width: 112 us [112;116] ( 28 S) 23 | [ 4] count: 2, width: 256 us [232;280] ( 64 S) 24 | [ 5] count: 1, width: 348 us [348;348] ( 87 S) 25 | Pulse period distribution: 26 | [ 0] count: 1, width: 48 us [48;48] ( 12 S) 27 | [ 1] count: 27, width: 116 us [116;120] ( 29 S) 28 | [ 2] count: 9, width: 232 us [232;232] ( 58 S) 29 | [ 3] count: 3, width: 172 us [172;176] ( 43 S) 30 | [ 4] count: 2, width: 576 us [520;636] ( 144 S) 31 | [ 5] count: 3, width: 308 us [292;340] ( 77 S) 32 | [ 6] count: 1, width: 872 us [872;872] ( 218 S) 33 | [ 7] count: 1, width: 1448 us [1448;1448] ( 362 S) 34 | Pulse timing distribution: 35 | [ 0] count: 1, width: 0 us [0;0] ( 0 S) 36 | [ 1] count: 71, width: 56 us [48;68] ( 14 S) 37 | [ 2] count: 6, width: 116 us [112;120] ( 29 S) 38 | [ 3] count: 9, width: 172 us [172;176] ( 43 S) 39 | [ 4] count: 3, width: 444 us [404;468] ( 111 S) 40 | [ 5] count: 3, width: 264 us [232;288] ( 66 S) 41 | [ 6] count: 1, width: 1392 us [1392;1392] ( 348 S) 42 | [ 7] count: 1, width: 348 us [348;348] ( 87 S) 43 | [ 8] count: 1, width: 0 us [0;0] ( 0 S) 44 | Level estimates [high, low]: 15873, 7 45 | RSSI: -0.1 dB SNR: 33.6 dB Noise: -33.7 dB 46 | Frequency offsets [F1, F2]: 7357, -7395 (+28.1 kHz, -28.2 kHz) 47 | Guessing modulation: Pulse Code Modulation (Not Return to Zero) 48 | Attempting demodulation... short_width: 56, long_width: 56, reset_limit: 57344, sync_width: 0 49 | Use a flex decoder with -X 'n=name,m=FSK_PCM,s=56,l=56,r=57344' 50 | pulse_demod_pcm(): Analyzer Device 51 | bitbuffer:: Number of rows: 1 52 | [00] {177} 55 55 55 55 55 51 6e a2 88 06 50 e8 8b f8 07 c0 a7 ff ff fb 9a 20 80 53 | 54 | Noting that the bit buffer seems to be out of sync by 1 bit. 55 <<1 = AA (Sync), 55 | 55 55 55 55 5 = AA AA AA AA A - sync 56 | 5 51 6e a = AA 2D D4 - pre 57 | etc 58 | 59 | Given that this actually has a well defined packet 60 | Data format: 61 | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 62 | aa aa aa 2d d4 51 00 6b 58 6e 7f 24 f8 d2 ff ff ff 3c 28 8 63 | FF II II II TB YY MM ZA AA XX XX XX CC SS 64 | Sync: aa aa aa ... 65 | Preamble: 2d d4 - actually preamble[] = {0xAA, 0x2D, 0xD4}; 66 | FF: Family code 0x51 (ECOWITT/FineOffset WH51) 67 | IIIIII: ID (3 bytes) 68 | T: Transmission period boost: highest 3 bits set to 111 on moisture change and decremented each transmission; 69 | if T = 0 period is 70 sec, if T > 0 period is 10 sec 70 | B: Battery voltage: lowest 5 bits are battery voltage * 10 (e.g. 0x0c = 12 = 1.2V). Transmitter works down to 0.7V (0x07) 71 | YY: ? Fixed: 0x7f 72 | MM: Moisture percentage 0%-100% (0x00-0x64) MM = (AD - 70) / (450 - 70) 73 | Z: ? Fixed: leftmost 7 bit 1111 100 74 | AAA: 9 bit AD value MSB byte[07] & 0x01, LSB byte[08] 75 | XXXXXX: ? Fixed: 0xff 0xff 0xff 76 | CC: CRC of the preceding 12 bytes (Polynomial 0x31, Initial value 0x00, Input not reflected, Result not reflected) 77 | SS: Sum of the preceding 13 bytes % 256 78 | 79 | It should be simple enough to set up the parameters in the normal packet TX code. I will do it tonight - I can verify against RTL_433 using the SDR, the Misol receiver and ESP. 80 | Hopefully, the transmitter (WH51) will be stable enough clock wise to use Packet Rx mode on the CC101. I have not tried to date but wanted to start with the RTL_433 approach. 81 | 82 | 83 | ## Tks to @AusGunno 84 | -------------------------------------------------------------------------------- /src/rtl_433/devices/auriol_hg02832.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Auriol HG02832 sensor. 3 | 4 | Copyright (C) 2019 Christian W. Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | /** 13 | Lidl Auriol HG02832 sensor, also Rubicson 48957 (Transmitter for 48956). 14 | 15 | S.a. (#1161), (#1205). 16 | 17 | Also works for the newer version HG05124A-DCF, IAN 321304_1901, version 07/2019. 18 | However, the display occasionally shows 0.1 C incorrectly, especially with odd values. 19 | But this is not an error of the evaluation of a single message, the sensor sends it this way. 20 | Perhaps the value is averaged in the station. 21 | 22 | PWM with 252 us short, 612 us long, and 860 us sync. 23 | Preamble is a long pulsem, then 3 times sync pulse, sync gap, then data. 24 | The 61ms packet gap is too long to capture repeats in one bitbuffer. 25 | 26 | Data layout: 27 | 28 | II HH F TTT CC 29 | 30 | - I: id, 8 bit 31 | - H: humidity, 8 bit 32 | - F: flags, 4 bit (Batt, TX-Button, Chan, Chan) 33 | - T: temperature, 12 bit, deg. C scale 10 34 | - C: checksum, 8 bit 35 | 36 | */ 37 | 38 | #include "decoder.h" 39 | 40 | static int auriol_hg02832_decode(r_device *decoder, bitbuffer_t *bitbuffer) 41 | { 42 | data_t *data; 43 | uint8_t *b; 44 | int id, humidity, batt_low, button, channel; 45 | int temp_raw; 46 | float temp_c; 47 | 48 | if (bitbuffer->num_rows != 2) 49 | return DECODE_ABORT_EARLY; 50 | if (bitbuffer->bits_per_row[0] != 1 || bitbuffer->bits_per_row[1] != 40) 51 | return DECODE_ABORT_LENGTH; 52 | 53 | bitbuffer_invert(bitbuffer); 54 | 55 | b = bitbuffer->bb[1]; 56 | 57 | // They tried to implement CRC-8 poly 0x31, but (accidentally?) reset the key every new byte. 58 | // (equivalent key stream is 7a 3d 86 43 b9 c4 62 31 repeated 4 times.) 59 | uint8_t d0 = b[0] ^ b[1] ^ b[2] ^ b[3]; 60 | uint8_t chk = crc8(&d0, 1, 0x31, 0x53) ^ b[4]; 61 | 62 | if (chk) 63 | return DECODE_FAIL_MIC; // prevent false positive checksum 64 | 65 | id = b[0]; 66 | humidity = b[1]; 67 | //flags = b[2] >> 4; 68 | batt_low = (b[2]) >> 7; 69 | button = (b[2] & 0x40) >> 6; 70 | channel = (b[2] & 0x30) >> 4; 71 | 72 | temp_raw = (int16_t)(((b[2] & 0x0f) << 12) | (b[3] << 4)); // uses sign extend 73 | temp_c = (temp_raw >> 4) * 0.1f; 74 | 75 | /* clang-format off */ 76 | data = data_make( 77 | "model", "", DATA_STRING, "Auriol-HG02832", 78 | "id", "", DATA_INT, id, 79 | "channel", "", DATA_INT, channel + 1, 80 | "battery_ok", "Battery", DATA_INT, !batt_low, 81 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, 82 | "humidity", "Humidity", DATA_FORMAT, "%.0f %%", DATA_DOUBLE, (float)humidity, 83 | "button", "Button", DATA_INT, button, 84 | "mic", "Integrity", DATA_STRING, "CRC", 85 | NULL); 86 | /* clang-format on */ 87 | 88 | decoder_output_data(decoder, data); 89 | return 1; 90 | } 91 | 92 | static char *output_fields[] = { 93 | "model", 94 | "id", 95 | "channel", 96 | "battery_ok", 97 | "temperature_C", 98 | "humidity", 99 | "button", 100 | "mic", 101 | NULL, 102 | }; 103 | 104 | r_device auriol_hg02832 = { 105 | .name = "Auriol HG02832, HG05124A-DCF, Rubicson 48957 temperature/humidity sensor", 106 | .modulation = OOK_PULSE_PWM, 107 | .short_width = 252, 108 | .long_width = 612, 109 | .sync_width = 860, 110 | .gap_limit = 750, 111 | .reset_limit = 62990, // 61ms packet gap 112 | .decode_fn = &auriol_hg02832_decode, 113 | .disabled = 0, 114 | .fields = output_fields, 115 | }; 116 | -------------------------------------------------------------------------------- /src/rtl_433/devices/tfa_30_3221.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Temperature/Humidity outdoor sensor TFA 30.3221.02. 3 | 4 | Copyright (C) 2020 Odessa Claude 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Temperature/Humidity outdoor sensor TFA 30.3221.02. 13 | 14 | This is the same as LaCrosse-TX141THBv2 and should be merged. 15 | 16 | S.a. https://github.com/RFD-FHEM/RFFHEM/blob/master/FHEM/14_SD_WS.pm 17 | 18 | 0 4 | 8 12 | 16 20 | 24 28 | 32 36 19 | --------- | --------- | --------- | --------- | --------- 20 | 0000 1001 | 0001 0110 | 0001 0000 | 0000 0111 | 0100 1001 21 | iiii iiii | bscc tttt | tttt tttt | hhhh hhhh | ???? ???? 22 | 23 | - i: 8 bit random id (changes on power-loss) 24 | - b: 1 bit battery indicator (0=>OK, 1=>LOW) 25 | - s: 1 bit sendmode (0=>auto, 1=>manual) 26 | - c: 2 bit channel valid channels are 0-2 (1-3) 27 | - t: 12 bit unsigned temperature, offset 500, scaled by 10 28 | - h: 8 bit relative humidity percentage 29 | - ?: 8 bit checksum digest 0x31, 0xf4 30 | 31 | The sensor sends 3 repetitions at intervals of about 60 seconds. 32 | */ 33 | 34 | #include "decoder.h" 35 | 36 | static int tfa_303221_callback(r_device *decoder, bitbuffer_t *bitbuffer) 37 | { 38 | int row, sendmode, channel, battery_low, temp_raw, humidity; 39 | float temp_c; 40 | data_t *data; 41 | uint8_t *b; 42 | unsigned int device; 43 | 44 | // Device send 4 row, checking for two repeated 45 | row = bitbuffer_find_repeated_row(bitbuffer, (bitbuffer->num_rows > 4) ? 4 : 2, 40); 46 | if (row < 0) 47 | return DECODE_ABORT_EARLY; 48 | 49 | // Checking for right number of bits per row 50 | if (bitbuffer->bits_per_row[row] > 41) 51 | return DECODE_ABORT_LENGTH; 52 | 53 | bitbuffer_invert(bitbuffer); 54 | b = bitbuffer->bb[row]; 55 | 56 | device = b[0]; 57 | 58 | // Sanity Check 59 | if (device == 0) 60 | return DECODE_FAIL_SANITY; 61 | 62 | // Validate checksum 63 | int observed_checksum = b[4]; 64 | int computed_checksum = lfsr_digest8_reflect(b, 4, 0x31, 0xf4); 65 | if (observed_checksum != computed_checksum) { 66 | return DECODE_FAIL_MIC; 67 | } 68 | 69 | temp_raw = ((b[1] & 0x0F) << 8) | b[2]; 70 | temp_c = (temp_raw - 500) * 0.1f; 71 | humidity = b[3]; 72 | battery_low = b[1] >> 7; 73 | channel = ((b[1] >> 4) & 3) + 1; 74 | sendmode = (b[1] >> 6) & 1; 75 | 76 | /* clang-format off */ 77 | data = data_make( 78 | "model", "", DATA_STRING, "TFA-303221", 79 | "id", "Sensor ID", DATA_INT, device, 80 | "channel", "Channel", DATA_INT, channel, 81 | "battery_ok", "Battery", DATA_INT, !battery_low, 82 | "temperature_C", "Temperature", DATA_FORMAT, "%.2f C", DATA_DOUBLE, temp_c, 83 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, 84 | "sendmode", "Test mode", DATA_INT, sendmode, 85 | "mic", "Integrity", DATA_STRING, "CRC", 86 | NULL); 87 | /* clang-format on */ 88 | 89 | decoder_output_data(decoder, data); 90 | return 1; 91 | } 92 | 93 | static char *output_fields[] = { 94 | "model", 95 | "id", 96 | "channel", 97 | "battery_ok", 98 | "temperature_C", 99 | "humidity", 100 | "sendmode", 101 | "mic", 102 | NULL, 103 | }; 104 | 105 | r_device tfa_30_3221 = { 106 | .name = "TFA Dostmann 30.3221.02 T/H Outdoor Sensor", 107 | .modulation = OOK_PULSE_PWM, 108 | .short_width = 235, 109 | .long_width = 480, 110 | .reset_limit = 850, 111 | .sync_width = 836, 112 | .decode_fn = &tfa_303221_callback, 113 | .priority = 10, // This is the same as LaCrosse-TX141THBv2 114 | .fields = output_fields, 115 | }; 116 | -------------------------------------------------------------------------------- /src/rtl_433/devices/efth800.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Eurochron EFTH-800 temperature and humidity sensor. 3 | 4 | Copyright (c) 2020 by Christian W. Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Eurochron EFTH-800 temperature and humidity sensor. 13 | 14 | Wakeup of short pulse, 4x 970 us gap, 990 us pulse, 15 | packet gap of 4900 us, 16 | two packets of each 17 | 4x 750 us pulse, 720 us gap, then 18 | (1-bit) 500 us pulse, 230 us gap or 19 | (0-bit) 250 us pulse, 480 us gap. 20 | 21 | Data layout: 22 | 23 | ?ccc iiii iiii iiii bntt tttt tttt ???? hhhh hhhh xxxx xxxx 24 | 25 | - c: 3 bit channel valid channels are 0-7 (stands for channel 1-8) 26 | - i: 12 bit random id (changes on power-loss) 27 | - b: 1 bit battery indicator (0=>OK, 1=>LOW) 28 | - n: 1 bit temperature sign? (0=>negative, 1=>positive) 29 | - t: 10 bit signed temperature, scaled by 10 30 | - h: 8 bit relative humidity percentage (BCD) 31 | - x: 8 bit CRC-8, poly 0x31, init 0x00 32 | - ?: unknown (Bit 0, 28-31 always 0 ?) 33 | 34 | The sensor sends messages at intervals of about 57-58 seconds. 35 | */ 36 | 37 | #include "decoder.h" 38 | 39 | static int eurochron_efth800_decode(r_device *decoder, bitbuffer_t *bitbuffer) 40 | { 41 | data_t *data; 42 | int row; 43 | uint8_t *b; 44 | int id, channel, temp_raw, humidity, battery_low; 45 | float temp_c; 46 | 47 | /* Validation checks */ 48 | row = bitbuffer_find_repeated_row(bitbuffer, 2, 48); 49 | 50 | if (row < 0) // repeated rows? 51 | return DECODE_ABORT_EARLY; 52 | 53 | if (bitbuffer->bits_per_row[row] > 49) // 48 bits per row? 54 | return DECODE_ABORT_LENGTH; 55 | 56 | b = bitbuffer->bb[row]; 57 | 58 | // This is actially a 0x00 packet error ( bitbuffer_invert ) 59 | // No need to decode/extract values for simple test 60 | if ( b[0] == 0xff && b[1] == 0xff && b[2] == 0xFF && b[4] == 0xFF ) { 61 | if (decoder->verbose > 1) { 62 | fprintf(stderr, "%s: DECODE_FAIL_SANITY data all 0xff\n", __func__); 63 | } 64 | return DECODE_FAIL_SANITY; 65 | } 66 | 67 | bitbuffer_invert(bitbuffer); 68 | 69 | if (crc8(b, 6, 0x31, 0x00)) 70 | return DECODE_FAIL_MIC; // crc mismatch 71 | 72 | /* Extract data */ 73 | channel = (b[0] & 0x70) >> 4; 74 | id = ((b[0] & 0x0f) << 8) | b[1]; 75 | battery_low = b[2] >> 7; 76 | temp_raw = (int16_t)((b[2] & 0x3f) << 10) | ((b[3] & 0xf0) << 2); // sign-extend 77 | temp_c = (temp_raw >> 6) * 0.1f; 78 | humidity = (b[4] >> 4) * 10 + (b[4] & 0xf); // BCD 79 | 80 | /* clang-format off */ 81 | data = data_make( 82 | "model", "", DATA_STRING, "Eurochron-EFTH800", 83 | "id", "", DATA_INT, id, 84 | "channel", "", DATA_INT, channel + 1, 85 | "battery_ok", "Battery", DATA_INT, !battery_low, 86 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, 87 | "humidity", "Humidity", DATA_INT, humidity, 88 | "mic", "Integrity", DATA_STRING, "CRC", 89 | NULL); 90 | /* clang-format on */ 91 | 92 | decoder_output_data(decoder, data); 93 | return 1; 94 | } 95 | 96 | static char *output_fields[] = { 97 | "model", 98 | "id", 99 | "channel", 100 | "battery_ok", 101 | "temperature_C", 102 | "humidity", 103 | "mic", 104 | NULL, 105 | }; 106 | 107 | r_device eurochron_efth800 = { 108 | .name = "Eurochron EFTH-800 temperature and humidity sensor", 109 | .modulation = OOK_PULSE_PWM, 110 | .short_width = 250, 111 | .long_width = 500, 112 | .sync_width = 750, 113 | .gap_limit = 900, 114 | .reset_limit = 5500, 115 | .decode_fn = &eurochron_efth800_decode, 116 | .fields = output_fields, 117 | }; 118 | -------------------------------------------------------------------------------- /src/rtl_433/devices/wssensor.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Hyundai WS SENZOR Remote Temperature Sensor. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | */ 9 | /** 10 | Hyundai WS SENZOR Remote Temperature Sensor. 11 | 12 | - Transmit Interval: every ~33s 13 | - Frequency 433.92 MHz 14 | - Distance coding: Pulse length 224 us 15 | - Short distance: 1032 us, long distance: 1992 us, packet distance: 4016 us 16 | 17 | 24-bit data packet format, repeated 23 times 18 | 19 | TTTTTTTT TTTTBSCC IIIIIIII 20 | 21 | - T = signed temperature * 10 in Celsius 22 | - B = battery status (0 = low, 1 = OK) 23 | - S = startup (0 = normal operation, 1 = battery inserted or TX button pressed) 24 | - C = channel (0-2) 25 | - I = sensor ID 26 | */ 27 | 28 | #include "decoder.h" 29 | 30 | #define WS_PACKETLEN 24 31 | #define WS_MINREPEATS 4 32 | #define WS_REPEATS 23 33 | 34 | static int wssensor_decode(r_device *decoder, bitbuffer_t *bitbuffer) 35 | { 36 | uint8_t *b; 37 | data_t *data; 38 | 39 | // the signal should have 23 repeats 40 | // require at least 4 received repeats 41 | int r = bitbuffer_find_repeated_row(bitbuffer, WS_MINREPEATS, WS_REPEATS); 42 | if (r < 0 || bitbuffer->bits_per_row[r] != WS_PACKETLEN) 43 | return DECODE_ABORT_LENGTH; 44 | 45 | b = bitbuffer->bb[r]; 46 | 47 | // No need to decode/extract values for simple test 48 | if ((!b[0] && !b[1] && !b[2]) 49 | || (b[0] == 0xff && b[1] == 0xff && b[2] == 0xff)) { 50 | if (decoder->verbose > 1) { 51 | fprintf(stderr, "%s: DECODE_FAIL_SANITY data all 0x00 or 0xFF\n", __func__); 52 | } 53 | return DECODE_FAIL_SANITY; 54 | } 55 | 56 | int temperature; 57 | int battery_status; 58 | int startup; 59 | int channel; 60 | int sensor_id; 61 | float temperature_c; 62 | 63 | /* TTTTTTTT TTTTBSCC IIIIIIII */ 64 | temperature = (int16_t)((b[0] << 8) | (b[1] & 0xf0)); // uses sign extend 65 | battery_status = (b[1] & 0x08) >> 3; 66 | startup = (b[1] & 0x04) >> 2; 67 | channel = (b[1] & 0x03) + 1; 68 | sensor_id = b[2]; 69 | 70 | temperature_c = (temperature >> 4) * 0.1f; 71 | 72 | if (decoder->verbose) { 73 | fprintf(stderr, "Hyundai WS SENZOR received raw data:\n"); 74 | bitbuffer_print(bitbuffer); 75 | fprintf(stderr, "Sensor ID = %01d = 0x%02x\n", sensor_id, sensor_id); 76 | fprintf(stderr, "Bitstream HEX = "); 77 | bitrow_print(b, 24); 78 | fprintf(stderr, "Battery OK = %0d\n", battery_status); 79 | fprintf(stderr, "Startup = %0d\n", startup); 80 | fprintf(stderr, "Channel = %0d\n", channel); 81 | fprintf(stderr, "temp = %d = 0x%02x\n", temperature, temperature); 82 | fprintf(stderr, "TemperatureC = %.1f\n", temperature_c); 83 | } 84 | 85 | /* clang-format off */ 86 | data = data_make( 87 | "model", "", DATA_STRING, "Hyundai-WS", 88 | "id", "House Code", DATA_INT, sensor_id, 89 | "channel", "Channel", DATA_INT, channel, 90 | "battery_ok", "Battery", DATA_INT, !!battery_status, 91 | "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temperature_c, 92 | "button", "Button", DATA_INT, startup, 93 | NULL); 94 | /* clang-format on */ 95 | 96 | decoder_output_data(decoder, data); 97 | return 1; 98 | } 99 | 100 | static char *output_fields[] = { 101 | "model", 102 | "id", 103 | "channel", 104 | "battery_ok", 105 | "temperature_C", 106 | "button", 107 | NULL, 108 | }; 109 | 110 | r_device wssensor = { 111 | .name = "Hyundai WS SENZOR Remote Temperature Sensor", 112 | .modulation = OOK_PULSE_PPM, 113 | .short_width = 1000, 114 | .long_width = 2000, 115 | .gap_limit = 2400, 116 | .reset_limit = 4400, 117 | .decode_fn = &wssensor_decode, 118 | .disabled = 0, 119 | .fields = output_fields, 120 | }; 121 | -------------------------------------------------------------------------------- /tools/update_rtl_433_devices.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | export MODULATION="OOK_PULSE_PWM|OOK_PULSE_PPM" 4 | 5 | rm copy.list devices.list rtl_433_ESP.fragment 6 | 7 | ( cd .. ; rm -rf rtl_433 ; git clone https://github.com/merbanan/rtl_433 ) 8 | ( cd ../rtl_433/src/devices/ ; egrep "\.name|\.modulation|\.decode_fn|^r_device " *.c ) |\ 9 | awk -f device.awk | egrep ${MODULATION} | awk -F : '{ print $1 }' | sort | uniq > copy.list 10 | 11 | # add flex decoder to the list 12 | 13 | echo "flex.c" >> copy.list 14 | 15 | # Populate src/rtl_433/device 16 | 17 | ( cd ../src/rtl_433/devices ; rm * ) 18 | 19 | for i in `cat copy.list` 20 | do 21 | cp ../rtl_433/src/devices/$i ../src/rtl_433/devices 22 | done 23 | 24 | for i in `ls ../contrib/` 25 | do 26 | cp ../contrib/$i ../src/rtl_433/devices 27 | done 28 | 29 | # remove non-functional device decoders 30 | 31 | # device decoders that place a bitbuffer_t on the stack is causing an overflow or large memory usage 32 | # ie bitbuffer_t databits = {0}; 33 | # ie blueline.c - approx 64000 bytes of memory used 34 | 35 | for i in newkaku.c nexa.c proove.c cavius.c current_cost.c ge_coloreffects.c insteon.c m_bus.c oil_standard.c oil_watchman.c tpms_abarth124.c tpms_citroen.c tpms_elantra2012.c tpms_ford.c tpms_jansite.c tpms_jansite_solar.c tpms_pmv107j.c tpms_renault.c tpms_toyota.c blueline.c 36 | do 37 | rm ../src/rtl_433/devices/$i 38 | done 39 | 40 | # create include/rtl_433_devices.h 41 | 42 | ( cd ../src/rtl_433/devices ; egrep "\.name|\.modulation|\.decode_fn|^r_device " *.c ) > devices.list 43 | 44 | COUNT=`cat devices.list | awk -f device.awk | egrep ${MODULATION} | awk -F\" '{ print $3 }' | awk -F, '{ print $3 }' | wc | awk '{ print $1 }'` 45 | 46 | cat devices.list | awk -f device.awk | egrep ${MODULATION} | awk -F\" '{ print $3 }' | \ 47 | awk -F, '{ print $3 }' | awk '{ print " DECL("$1") \\" }' > rtl_433_devices.fragment 48 | 49 | echo " /* Add new decoders here. */" >> rtl_433_devices.fragment 50 | 51 | echo "#define NUMOFDEVICES ${COUNT}" >> rtl_433_devices.fragment 52 | 53 | cat rtl_433_devices.pre rtl_433_devices.fragment rtl_433_devices.post > ../include/rtl_433_devices.h 54 | 55 | # create src/rtl_433_ESP.cpp fragment 56 | 57 | echo " // This is a generated fragment from tools/update_rtl_433_devices.sh" > rtl_433_ESP.fragment 58 | 59 | echo "" >> rtl_433_ESP.fragment 60 | 61 | cat devices.list | awk -f device.awk | egrep ${MODULATION} | awk -F\" '{ print $3 }' | \ 62 | awk -F, '{ print $3 }' | awk '{ print " memcpy(&cfg->devices["NR-1"], &"$1", sizeof(r_device));" }' >> rtl_433_ESP.fragment 63 | 64 | echo "" >> rtl_433_ESP.fragment 65 | 66 | echo " // end of fragement" >> rtl_433_ESP.fragment 67 | 68 | echo 69 | echo "Please update src/rtl_433_ESP.cpp with rtl_433_ESP.fragment" 70 | 71 | # copy src files from rtl_433/src to src/rtl_433 72 | 73 | for i in abuf.c bitbuffer.c decoder_util.c list.c r_util.c util.c optparse.c 74 | do 75 | cp ../rtl_433/src/$i ../src/rtl_433 76 | done 77 | 78 | # copy include files from rtl_433/include to include 79 | 80 | for i in abuf.h am_analyze.h baseband.h bitbuffer.h compat_time.h decoder.h decoder_util.h fatal.h fileformat.h list.h optparse.h pulse_demod.h r_api.h r_util.h samp_grab.h term_ctl.h util.h 81 | do 82 | cp ../rtl_433/include/$i ../include 83 | done 84 | 85 | echo "These source files need updating" 86 | 87 | echo "data.c - Defined out unneeded functions ( #ifndef ESP32 )" 88 | echo "pulse_demod.c - Move 'bitbuffer_t bits' to class level" 89 | echo "r_api.c - Significant tuning and tweaking applied" 90 | 91 | echo 92 | echo "cp ../../rtl_433/src/data.c ." 93 | echo "cp ../../rtl_433/src/pulse_demod.c ." 94 | echo "cp ../../rtl_433/src/r_api.c ." 95 | echo 96 | echo "These include files need updating" 97 | 98 | echo "data.h - Added '#define _POSIX_HOST_NAME_MAX 128'" 99 | echo "log.h - Not from rtl_433.h" 100 | echo "pulse_detect.h - Adjusted structures to reduce size" 101 | echo "r_device.h - Adjusted structures to reduce size" 102 | echo "r_private.h - Adjusted structures to reduce size" 103 | echo "rtl_433.h - Adjusted structures to reduce size" 104 | 105 | echo 106 | 107 | echo "cp ../rtl_433/include/pulse_detect.h ." 108 | echo "cp ../rtl_433/include/data.h ." 109 | echo "cp ../rtl_433/include/r_device.h ." 110 | echo "cp ../rtl_433/include/r_private.h ." 111 | echo "cp ../rtl_433/include/rtl_433.h ." 112 | -------------------------------------------------------------------------------- /src/rtl_433/devices/bt_rain.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Biltema-Rain sensor. 3 | 4 | Copyright (C) 2017 Timopen, cleanup by Benjamin Larsson 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Biltema-Rain sensor. 13 | 14 | Based on the springfield.c code, there is a lack of samples and data 15 | thus the decoder is disabled by default. 16 | 17 | - nibble[0] and nibble[1] is the id, changes with every reset. 18 | - nibble[2] first bit is battery (0=OK). 19 | - nibble[3] bit 1 is tx button pressed. 20 | - nibble[3] bit 2 = below zero, subtract temperature with 1024. I.e. 11 bit 2's complement. 21 | - nibble[3](bit 3 and 4) + nibble[4] + nibble[5] is the temperature in Celsius with one decimal. 22 | - nibble[2](bit 2-4) + nibble[6] + nibble[7] is the rain rate, increases 25!? with every tilt of 23 | the teeter (1.3 mm rain) after 82 tilts it starts over but carries the rest to the next round 24 | e.g tilt 82 = 2 divide by 19.23 to get mm. 25 | - nibble[8] is checksum, have not figured it out yet. Last bit is sync? or included in checksum?. 26 | */ 27 | 28 | #include "decoder.h" 29 | 30 | // Actually 37 bits for all but last transmission which is 36 bits 31 | #define NUM_BITS 36 32 | 33 | static int bt_rain_decode(r_device *decoder, bitbuffer_t *bitbuffer) 34 | { 35 | data_t *data; 36 | uint8_t *b; 37 | int row; 38 | int id, battery, rain, button, channel; 39 | int temp_raw; 40 | float temp_c, rainrate; 41 | 42 | row = bitbuffer_find_repeated_row(bitbuffer, 4, NUM_BITS); 43 | if (row < 0) 44 | return DECODE_ABORT_EARLY; 45 | 46 | if (bitbuffer->bits_per_row[row] != NUM_BITS && bitbuffer->bits_per_row[row] != NUM_BITS + 1) 47 | return DECODE_ABORT_LENGTH; 48 | 49 | b = bitbuffer->bb[row]; 50 | 51 | if (b[0] == 0xff && b[1] == 0xff && b[2] == 0xff && b[3] == 0xff) 52 | return DECODE_FAIL_SANITY; // prevent false positive checksum 53 | 54 | id = b[0]; 55 | battery = b[1] >> 7; 56 | channel = ((b[1] & 0x30) >> 4) + 1; // either this or the rain top bits could be wrong 57 | button = (b[1] & 0x08) >> 3; 58 | 59 | temp_raw = (int16_t)(((b[1] & 0x07) << 13) | (b[2] << 5)); // uses sign extend 60 | temp_c = (temp_raw >> 5) * 0.1f; 61 | 62 | rain = ((b[1] & 0x07) << 4) | b[3]; // either b[1] or the channel above bould be wrong 63 | int rest = rain % 25; 64 | if (rest % 2) 65 | rain += ((rest / 2) * 2048); 66 | else 67 | rain += ((rest + 1) / 2) * 2048 + 12 * 2048; 68 | rainrate = rain * 0.052f; // 19.23mm per tip 69 | 70 | /* clang-format off */ 71 | data = data_make( 72 | "model", "", DATA_STRING, "Biltema-Rain", 73 | "id", "ID", DATA_INT, id, 74 | "channel", "Channel", DATA_INT, channel, 75 | "battery_ok", "Battery", DATA_INT, !battery, 76 | "transmit", "Transmit", DATA_STRING, button ? "MANUAL" : "AUTO", // TODO: delete this 77 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, 78 | "rain_rate_mm_h", "Rain per hour", DATA_FORMAT, "%.02f mm/h", DATA_DOUBLE, rainrate, 79 | "button", "Button", DATA_INT, button, 80 | NULL); 81 | /* clang-format on */ 82 | 83 | decoder_output_data(decoder, data); 84 | return 1; 85 | } 86 | 87 | static char *output_fields[] = { 88 | "model", 89 | "id", 90 | "channel", 91 | "battery_ok", 92 | "transmit", // TODO: delete this 93 | "temperature_C", 94 | "rain_rate_mm_h", 95 | "button", 96 | NULL, 97 | }; 98 | 99 | r_device bt_rain = { 100 | .name = "Biltema rain gauge", 101 | .modulation = OOK_PULSE_PPM, 102 | .short_width = 1940, 103 | .long_width = 3900, 104 | .gap_limit = 4100, 105 | .reset_limit = 8800, 106 | .decode_fn = &bt_rain_decode, 107 | .disabled = 1, 108 | .fields = output_fields}; 109 | -------------------------------------------------------------------------------- /src/rtl_433/devices/prologue.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Prologue sensor protocol. 3 | */ 4 | /** @fn int prologue_callback(r_device *decoder, bitbuffer_t *bitbuffer) 5 | Prologue sensor protocol, 6 | also FreeTec NC-7104 sensor for FreeTec Weatherstation NC-7102, 7 | and Pearl NC-7159-675. 8 | The sensor can be bought at Clas Ohlson. 9 | 10 | Note: this is a false positive for AlectoV1. 11 | 12 | The sensor sends 36 bits 7 times, before the first packet there is a sync pulse. 13 | The packets are ppm modulated (distance coding) with a pulse of ~500 us 14 | followed by a short gap of ~2000 us for a 0 bit or a long ~4000 us gap for a 15 | 1 bit, the sync gap is ~9000 us. 16 | 17 | The data is grouped in 9 nibbles 18 | 19 | [type] [id0] [id1] [flags] [temp0] [temp1] [temp2] [humi0] [humi1] 20 | 21 | - type: 4 bit fixed 1001 (9) or 0110 (5) 22 | - id: 8 bit a random id that is generated when the sensor starts, could include battery status 23 | the same batteries often generate the same id 24 | - flags(3): is 0 when the battery is low, otherwise 1 (ok), first reading always says low 25 | - flags(2): is 1 when the sensor sends a reading when pressing the button on the sensor 26 | - flags(1,0): the channel number that can be set by the sensor (1, 2, 3, X) 27 | - temp: 12 bit signed scaled by 10 28 | - humi: 8 bit always 11001100 (0xCC) if no humidity sensor is available 29 | 30 | */ 31 | 32 | #include "decoder.h" 33 | 34 | static int prologue_callback(r_device *decoder, bitbuffer_t *bitbuffer) 35 | { 36 | uint8_t *b; 37 | data_t *data; 38 | 39 | int type; 40 | int id; 41 | int battery; 42 | int button; 43 | int channel; 44 | int temp_raw; 45 | int humidity; 46 | 47 | if (bitbuffer->bits_per_row[0] <= 8 && bitbuffer->bits_per_row[0] != 0) 48 | return DECODE_ABORT_EARLY; // Alecto/Auriol-v2 has 8 sync bits, reduce false positive 49 | 50 | int r = bitbuffer_find_repeated_row(bitbuffer, 4, 36); // only 3 repeats will give false positives for Alecto/Auriol-v2 51 | if (r < 0) 52 | return DECODE_ABORT_EARLY; 53 | 54 | if (bitbuffer->bits_per_row[r] > 37) // we expect 36 bits but there might be a trailing 0 bit 55 | return DECODE_ABORT_LENGTH; 56 | 57 | b = bitbuffer->bb[r]; 58 | 59 | if ((b[0] & 0xF0) != 0x90 && (b[0] & 0xF0) != 0x50) 60 | return DECODE_FAIL_SANITY; 61 | 62 | // Prologue/ThermoPro-TX2 sensor 63 | type = b[0] >> 4; 64 | id = ((b[0] & 0x0F) << 4) | ((b[1] & 0xF0) >> 4); 65 | battery = b[1] & 0x08; 66 | button = (b[1] & 0x04) >> 2; 67 | channel = (b[1] & 0x03) + 1; 68 | temp_raw = (int16_t)((b[2] << 8) | (b[3] & 0xF0)); // uses sign-extend 69 | temp_raw = temp_raw >> 4; 70 | humidity = ((b[3] & 0x0F) << 4) | (b[4] >> 4); 71 | 72 | /* clang-format off */ 73 | data = data_make( 74 | "model", "", DATA_STRING, "Prologue-TH", 75 | "subtype", "", DATA_INT, type, 76 | "id", "", DATA_INT, id, 77 | "channel", "Channel", DATA_INT, channel, 78 | "battery_ok", "Battery", DATA_INT, !!battery, 79 | "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_raw * 0.1, 80 | "humidity", "Humidity", DATA_COND, humidity != 0xcc, DATA_FORMAT, "%u %%", DATA_INT, humidity, 81 | "button", "Button", DATA_INT, button, 82 | NULL); 83 | /* clang-format on */ 84 | 85 | decoder_output_data(decoder, data); 86 | return 1; 87 | } 88 | 89 | static char *output_fields[] = { 90 | "model", 91 | "subtype", 92 | "id", 93 | "channel", 94 | "battery_ok", 95 | "temperature_C", 96 | "humidity", 97 | "button", 98 | NULL, 99 | }; 100 | 101 | r_device prologue = { 102 | .name = "Prologue, FreeTec NC-7104, NC-7159-675 temperature sensor", 103 | .modulation = OOK_PULSE_PPM, 104 | .short_width = 2000, 105 | .long_width = 4000, 106 | .gap_limit = 7000, 107 | .reset_limit = 10000, 108 | .decode_fn = &prologue_callback, 109 | .priority = 10, // Alecto collision, if Alecto checksum is correct it's not Prologue/ThermoPro-TX2 110 | .fields = output_fields, 111 | }; 112 | -------------------------------------------------------------------------------- /src/rtl_433/devices/dish_remote_6_3.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Decoder for UHF Dish Remote Control 6.3. 3 | 4 | Copyright (C) 2018 David E. Tiller 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | */ 12 | /** 13 | Decoder for UHF Dish Remote Control 6.3. 14 | (tested with genuine Dish remote.) 15 | 16 | The device uses PPM encoding, 17 | 0 is encoded as 400 us pulse and 1692 uS gap, 18 | 1 is encoded as 400 us pulse and 2812 uS gap. 19 | The device sends 7 transmissions per button press approx 6000 uS apart. 20 | A transmission starts with a 400 uS start bit and a 6000 uS gap. 21 | 22 | Each packet is 16 bits in length. 23 | Packet bits: BBBBBB10 101X1XXX 24 | B = Button pressed, big-endian 25 | X = unknown, possibly channel 26 | */ 27 | 28 | #include "decoder.h" 29 | 30 | #define MYDEVICE_BITLEN 16 31 | #define MYDEVICE_MINREPEATS 3 32 | 33 | char *button_map[] = { 34 | /* 0 */ "Undefined", 35 | /* 1 */ "Undefined", 36 | /* 2 */ "Swap", 37 | /* 3 */ "Undefined", 38 | /* 4 */ "Position", 39 | /* 5 */ "PIP", 40 | /* 6 */ "DVR", 41 | /* 7 */ "Undefined", 42 | /* 8 */ "Skip Forward", 43 | /* 9 */ "Skip Backward", 44 | /* 10 */ "Undefined", 45 | /* 11 */ "Dish Button", 46 | /* 12 */ "Undefined", 47 | /* 13 */ "Forward", 48 | /* 14 */ "Backward", 49 | /* 15 */ "TV Power", 50 | /* 16 */ "Reset", 51 | /* 17 */ "Undefined", 52 | /* 18 */ "Undefined", 53 | /* 19 */ "Undefined", 54 | /* 20 */ "Undefined", 55 | /* 21 */ "Undefined", 56 | /* 22 */ "SAT", 57 | /* 23 */ "Mute/Volume Up/Volume Down", 58 | /* 24 */ "Undefined", 59 | /* 25 */ "#/Search", 60 | /* 26 */ "*/Format", 61 | /* 27 */ "Undefined", 62 | /* 28 */ "Undefined", 63 | /* 29 */ "Undefined", 64 | /* 30 */ "Stop", 65 | /* 31 */ "Pause", 66 | /* 32 */ "Record", 67 | /* 33 */ "Channel Down", 68 | /* 34 */ "Undefined", 69 | /* 35 */ "Left", 70 | /* 36 */ "Recall", 71 | /* 37 */ "Channel Up", 72 | /* 38 */ "Undefined", 73 | /* 39 */ "Right", 74 | /* 40 */ "TV/Video", 75 | /* 41 */ "View/Live TV", 76 | /* 42 */ "Undefined", 77 | /* 43 */ "Guide", 78 | /* 44 */ "Undefined", 79 | /* 45 */ "Cancel", 80 | /* 46 */ "Digit 0", 81 | /* 47 */ "Select", 82 | /* 48 */ "Page Up", 83 | /* 49 */ "Digit 9", 84 | /* 50 */ "Digit 8", 85 | /* 51 */ "Digit 7", 86 | /* 52 */ "Menu", 87 | /* 53 */ "Digit 6", 88 | /* 54 */ "Digit 5", 89 | /* 55 */ "Digit 4", 90 | /* 56 */ "Page Down", 91 | /* 57 */ "Digit 3", 92 | /* 58 */ "Digit 2", 93 | /* 59 */ "Digit 1", 94 | /* 60 */ "Play", 95 | /* 61 */ "Dish Power", 96 | /* 62 */ "Undefined", 97 | /* 63 */ "Info" 98 | }; 99 | 100 | static int dish_remote_6_3_callback(r_device *decoder, bitbuffer_t *bitbuffer) 101 | { 102 | data_t *data; 103 | int r; // a row index 104 | uint8_t *b; // bits of a row 105 | uint8_t button; 106 | char *button_string; 107 | 108 | if (decoder->verbose > 1) { 109 | fprintf(stderr,"dish_remote_6_3_callback callback:\n"); 110 | bitbuffer_print(bitbuffer); 111 | } 112 | 113 | r = bitbuffer_find_repeated_row(bitbuffer, MYDEVICE_MINREPEATS, MYDEVICE_BITLEN); 114 | if (r < 0 || bitbuffer->bits_per_row[r] > MYDEVICE_BITLEN) { 115 | return DECODE_ABORT_LENGTH; 116 | } 117 | 118 | b = bitbuffer->bb[r]; 119 | 120 | /* Check fixed bits to prevent misreads */ 121 | if ((b[0] & 0x03) != 0x02 || (b[1] & 0xe8) != 0xa8) { 122 | return DECODE_FAIL_SANITY; 123 | } 124 | 125 | button = b[0] >> 2; 126 | button_string = button_map[button]; 127 | 128 | data = data_make( 129 | "model", "", DATA_STRING, "Dish-RC63", 130 | "button", "", DATA_STRING, button_string, 131 | NULL); 132 | 133 | decoder_output_data(decoder, data); 134 | 135 | return 1; 136 | } 137 | 138 | static char *output_fields[] = { 139 | "model", 140 | "button", 141 | NULL 142 | }; 143 | 144 | r_device dish_remote_6_3 = { 145 | .name = "Dish remote 6.3", 146 | .modulation = OOK_PULSE_PPM, 147 | .short_width = 1692, 148 | .long_width = 2812, 149 | .gap_limit = 4500, 150 | .reset_limit = 9000, 151 | .decode_fn = &dish_remote_6_3_callback, 152 | .disabled = 1, 153 | .fields = output_fields, 154 | }; 155 | -------------------------------------------------------------------------------- /src/rtl_433/devices/kedsum.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Kedsum temperature and humidity sensor (http://amzn.to/25IXeng). 3 | 4 | Copyright (C) 2016 John Lifsey 5 | Enhanced (C) 2019 Christian W. Zuckschwerdt 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | */ 12 | /** 13 | Largely the same as esperanza_ews, s3318p. 14 | @sa esperanza_ews.c s3318p.c 15 | 16 | My models transmit at a bit lower freq. of around 433.71 Mhz. 17 | Also NC-7415 from Pearl. 18 | 19 | Frame structure: 20 | 21 | Byte: 0 1 2 3 4 22 | Nibble: 1 2 3 4 5 6 7 8 9 10 23 | Type: 00 IIIIIIII BBCC++++ ttttTTTT hhhhHHHH FFFFXXXX 24 | 25 | - I: unique id. changes on powercycle 26 | - B: Battery state 10 = Ok, 01 = weak, 00 = bad 27 | - C: channel, 00 = ch1, 10=ch3 28 | - + low temp nibble 29 | - t: med temp nibble 30 | - T: high temp nibble 31 | - h: humidity low nibble 32 | - H: humidity high nibble 33 | - F: flags 34 | - X: CRC-4 poly 0x3 init 0x0 xor last 4 bits 35 | */ 36 | 37 | #include "decoder.h" 38 | 39 | static int kedsum_callback(r_device *decoder, bitbuffer_t *bitbuffer) 40 | { 41 | uint8_t b[5]; 42 | data_t *data; 43 | 44 | // the signal should start with 15 sync pulses (empty rows) 45 | // require at least 5 received syncs 46 | if (bitbuffer->num_rows < 5 47 | || bitbuffer->bits_per_row[0] != 0 48 | || bitbuffer->bits_per_row[1] != 0 49 | || bitbuffer->bits_per_row[2] != 0 50 | || bitbuffer->bits_per_row[3] != 0 51 | || bitbuffer->bits_per_row[4] != 0) 52 | return DECODE_ABORT_EARLY; 53 | 54 | // the signal should have 6 repeats with a sync pulse between 55 | // require at least 4 received repeats 56 | int r = bitbuffer_find_repeated_row(bitbuffer, 4, 42); 57 | if (r < 0 || bitbuffer->bits_per_row[r] != 42) 58 | return DECODE_ABORT_LENGTH; 59 | 60 | // remove the two leading 0-bits and align the data 61 | bitbuffer_extract_bytes(bitbuffer, r, 2, b, 40); 62 | 63 | // CRC-4 poly 0x3, init 0x0 over 32 bits then XOR the next 4 bits 64 | int crc = crc4(b, 4, 0x3, 0x0) ^ (b[4] >> 4); 65 | if (crc != (b[4] & 0xf)) 66 | return DECODE_FAIL_MIC; 67 | 68 | int id = (b[0]); 69 | int battery = (b[1] >> 6); // level 0-2 70 | int channel = ((b[1] & 0x30) >> 4) + 1; 71 | int temp_raw = ((b[2] & 0x0f) << 8) | (b[2] & 0xf0) | (b[1] & 0x0f); 72 | int humidity = ((b[3] & 0x0f) << 4) | ((b[3] & 0xf0) >> 4); 73 | float temp_f = (temp_raw - 900) * 0.1f; 74 | 75 | int flags = (b[1] & 0xc0) | (b[4] >> 4); 76 | 77 | battery = battery == 2 ? 100 : battery * 10; // level 0,1,2 -> 0,10,100 78 | 79 | /* clang-format off */ 80 | data = data_make( 81 | "model", "", DATA_STRING, "Kedsum-TH", 82 | "id", "ID", DATA_INT, id, 83 | "channel", "Channel", DATA_INT, channel, 84 | "battery_ok", "Battery level", DATA_DOUBLE, battery * 0.01f, 85 | "flags", "Flags2", DATA_INT, flags, 86 | "temperature_F", "Temperature", DATA_FORMAT, "%.02f F", DATA_DOUBLE, temp_f, 87 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, 88 | "mic", "Integrity", DATA_STRING, "CRC", 89 | NULL); 90 | /* clang-format on */ 91 | 92 | decoder_output_data(decoder, data); 93 | return 1; 94 | } 95 | 96 | static char *output_fields[] = { 97 | "model", 98 | "id", 99 | "channel", 100 | "battery_ok", 101 | "flags", 102 | "temperature_F", 103 | "humidity", 104 | "mic", 105 | NULL, 106 | }; 107 | 108 | r_device kedsum = { 109 | .name = "Kedsum Temperature & Humidity Sensor, Pearl NC-7415", 110 | .modulation = OOK_PULSE_PPM, 111 | .short_width = 2000, 112 | .long_width = 4000, 113 | .gap_limit = 4400, 114 | .reset_limit = 9400, 115 | .decode_fn = &kedsum_callback, 116 | .disabled = 0, 117 | .fields = output_fields, 118 | }; 119 | -------------------------------------------------------------------------------- /src/rtl_433/devices/kerui.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Kerui PIR / Contact Sensor. 3 | 4 | Copyright (C) 2016 Karl Lattimer 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Kerui PIR / Contact Sensor. 13 | 14 | Such as 15 | http://www.ebay.co.uk/sch/i.html?_from=R40&_trksid=p2050601.m570.l1313.TR0.TRC0.H0.Xkerui+pir.TRS0&_nkw=kerui+pir&_sacat=0 16 | 17 | also tested with: 18 | - KERUI D026 Window Door Magnet Sensor Detector (433MHz) https://fccid.io/2AGNGKR-D026 19 | events: open / close / tamper / battery low (below 5V of 12V battery) 20 | - Water leak sensor WD51 21 | - Mini Pir P831 22 | 23 | Note: simple 24 bit fixed ID protocol (x1527 style) and should be handled by the flex decoder. 24 | There is a leading sync bit with a wide gap which runs into the preceeding packet, it's ignored as 25th data bit. 25 | 26 | There are slight timing differences between the older sensors and new ones like Water leak sensor WD51 and Mini Pir P831. 27 | Long: 860-1016 us, short: 304-560 us, older sync: 480 us, newer sync: 340 us, 28 | */ 29 | 30 | #include "decoder.h" 31 | 32 | static int kerui_callback(r_device *decoder, bitbuffer_t *bitbuffer) 33 | { 34 | data_t *data; 35 | uint8_t *b; 36 | int id; 37 | int cmd; 38 | char *cmd_str; 39 | 40 | int r = bitbuffer_find_repeated_row(bitbuffer, 9, 25); // expected are 25 packets, require 9 41 | if (r < 0) 42 | return DECODE_ABORT_LENGTH; 43 | 44 | if (bitbuffer->bits_per_row[r] != 25) 45 | return DECODE_ABORT_LENGTH; 46 | b = bitbuffer->bb[r]; 47 | 48 | // No need to decode/extract values for simple test 49 | if ( !b[0] && !b[1] && !b[2] ) { 50 | if (decoder->verbose > 1) { 51 | fprintf(stderr, "%s: DECODE_FAIL_SANITY data all 0x00\n", __func__); 52 | } 53 | return DECODE_FAIL_SANITY; 54 | } 55 | 56 | //invert bits, short pulse is 0, long pulse is 1 57 | b[0] = ~b[0]; 58 | b[1] = ~b[1]; 59 | b[2] = ~b[2]; 60 | 61 | id = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); 62 | cmd = b[2] & 0x0F; 63 | switch (cmd) { 64 | case 0xa: cmd_str = "motion"; break; 65 | case 0xe: cmd_str = "open"; break; 66 | case 0x7: cmd_str = "close"; break; 67 | case 0xb: cmd_str = "tamper"; break; 68 | case 0x5: cmd_str = "water"; break; 69 | case 0xf: cmd_str = "battery"; break; 70 | default: cmd_str = NULL; break; 71 | } 72 | 73 | if (!cmd_str) 74 | return DECODE_ABORT_EARLY; 75 | 76 | /* clang-format off */ 77 | data = data_make( 78 | "model", "", DATA_STRING, "Kerui-Security", 79 | "id", "ID (20bit)", DATA_FORMAT, "0x%x", DATA_INT, id, 80 | "cmd", "Command (4bit)", DATA_FORMAT, "0x%x", DATA_INT, cmd, 81 | "motion", "", DATA_COND, cmd == 0xa, DATA_INT, 1, 82 | "opened", "", DATA_COND, cmd == 0xe, DATA_INT, 1, 83 | "opened", "", DATA_COND, cmd == 0x7, DATA_INT, 0, 84 | "tamper", "", DATA_COND, cmd == 0xb, DATA_INT, 1, 85 | "water", "", DATA_COND, cmd == 0x5, DATA_INT, 1, 86 | "battery_ok", "Battery", DATA_COND, cmd == 0xf, DATA_INT, 0, 87 | "state", "State", DATA_STRING, cmd_str, 88 | NULL); 89 | /* clang-format on */ 90 | 91 | decoder_output_data(decoder, data); 92 | return 1; 93 | } 94 | 95 | static char *output_fields[] = { 96 | "model", 97 | "id", 98 | "cmd", 99 | "motion", 100 | "opened", 101 | "tamper", 102 | "water", 103 | "battery_ok", 104 | "state", 105 | NULL, 106 | }; 107 | 108 | r_device kerui = { 109 | .name = "Kerui PIR / Contact Sensor", 110 | .modulation = OOK_PULSE_PWM, 111 | .short_width = 420, 112 | .long_width = 960, 113 | .gap_limit = 1100, 114 | .reset_limit = 9900, 115 | .tolerance = 160, 116 | .decode_fn = &kerui_callback, 117 | .fields = output_fields, 118 | }; 119 | -------------------------------------------------------------------------------- /src/rtl_433/devices/maverick_et73.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Maverick ET-73. 3 | 4 | Copyright (C) 2018 Benjamin Larsson 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Maverick ET-73. 13 | 14 | Based on TP12 code 15 | 16 | [00] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 17 | [01] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 18 | [02] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 19 | [03] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 20 | [04] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 21 | [05] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 22 | [06] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 23 | [07] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 24 | [08] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 25 | [09] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 26 | [10] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 27 | [11] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 28 | [12] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 29 | [13] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 30 | 31 | 32 | Layout appears to be: 33 | 34 | II 11 12 22 XX XX 35 | [01] {48} 68 00 01 0b 90 fc : 01101000 00000000 00000001 00001011 10010000 11111100 36 | 37 | - I = random id 38 | - 1 = temperature sensor 1 12 bits 39 | - 2 = temperature sensor 2 12 bits 40 | - X = unknown, checksum maybe ? 41 | 42 | */ 43 | 44 | #include "decoder.h" 45 | 46 | static int maverick_et73_sensor_callback(r_device *decoder, bitbuffer_t *bitbuffer) 47 | { 48 | int temp1_raw, temp2_raw, row; 49 | float temp1_c, temp2_c; 50 | uint8_t *bytes; 51 | unsigned int device; 52 | data_t *data; 53 | 54 | // The device transmits many rows, let's check for 3 matching. 55 | row = bitbuffer_find_repeated_row(bitbuffer, 3, 48); 56 | if (row < 0) { 57 | return DECODE_ABORT_EARLY; 58 | } 59 | 60 | bytes = bitbuffer->bb[row]; 61 | if ( (!bytes[0] && !bytes[1] && !bytes[2] && !bytes[3]) 62 | || (bytes[0] == 0xFF && bytes[1] == 0xFF && bytes[2] == 0xFF && bytes[3] == 0xFF)) { 63 | return DECODE_ABORT_EARLY; // reduce false positives 64 | } 65 | 66 | if (bitbuffer->bits_per_row[row] != 48) 67 | return DECODE_ABORT_LENGTH; 68 | 69 | device = bytes[0]; 70 | 71 | if (decoder->verbose) { 72 | fprintf(stderr,"maverick_et73_raw_data:"); 73 | bitrow_print(bytes, 48); 74 | } 75 | 76 | temp1_raw = (bytes[1] << 4) | ((bytes[2] & 0xf0) ); 77 | temp2_raw = ((bytes[2] & 0x0f) << 8) | bytes[3]; 78 | 79 | temp1_c = temp1_raw * 0.1f; 80 | temp2_c = temp2_raw * 0.1f; 81 | 82 | /* clang-format off */ 83 | data = data_make( 84 | "model", "", DATA_STRING, "Maverick-ET73", 85 | "id", "Random Id", DATA_INT, device, 86 | "temperature_1_C", "Temperature 1", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp1_c, 87 | "temperature_2_C", "Temperature 2", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp2_c, 88 | NULL); 89 | /* clang-format on */ 90 | 91 | decoder_output_data(decoder, data); 92 | return 1; 93 | } 94 | 95 | static char *output_fields[] = { 96 | "model", 97 | "id", 98 | "temperature_1_C", 99 | "temperature_2_C", 100 | NULL, 101 | }; 102 | 103 | r_device maverick_et73 = { 104 | .name = "Maverick et73", 105 | .modulation = OOK_PULSE_PPM, 106 | .short_width = 1050, 107 | .long_width = 2050, 108 | .gap_limit = 2200, 109 | .reset_limit = 4400, // 4050 us nominal packet gap 110 | .decode_fn = &maverick_et73_sensor_callback, 111 | .disabled = 0, 112 | .fields = output_fields, 113 | }; 114 | -------------------------------------------------------------------------------- /example/OOK_Receiver/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = esp32_heltec 13 | src_dir = . 14 | 15 | [libraries] 16 | arduinolog = https://github.com/1technophile/Arduino-Log.git#d13cd80 17 | arduinojson = ArduinoJson@5.13.4 18 | ; rtl_433_ESP = https://github.com/NorthernMan54/rtl_433_ESP.git#v0.1.3 // or file:///Users/xxxxxx/Code/rtl_433_ESP 19 | 20 | [env] 21 | framework = arduino 22 | platform = espressif32@3.3.1 23 | lib_ldf_mode = chain+ 24 | lib_deps = 25 | ${libraries.arduinolog} 26 | ${libraries.arduinojson} 27 | ${libraries.rtl_433_ESP} 28 | 29 | [env:esp32_cc1101] 30 | monitor_filters = esp32_exception_decoder 31 | board = esp32dev 32 | build_type = debug 33 | build_flags = 34 | '-DLOG_LEVEL=LOG_LEVEL_TRACE' 35 | '-DONBOARD_LED=2' ; My ESP32 board had this wiring 36 | ; *** rtl_433_ESP Options *** 37 | ; '-DRTL_DEBUG=4' ; rtl_433 verbose mode 38 | ; '-DRTL_VERBOSE=58' ; LaCrosse TX141-Bv2, TX141TH-Bv2, TX141-Bv3, TX141W, TX145wsdth sensor 39 | ; '-DRAW_SIGNAL_DEBUG=true' ; display raw received messages 40 | ; '-DMEMORY_DEBUG=true' ; display memory usage information 41 | '-DDEMOD_DEBUG=true' ; display signal debug info 42 | ; '-DMY_DEVICES=true' ; subset of devices 43 | '-DPUBLISH_UNPARSED=true' ; publish unparsed signal details 44 | '-DMINRSSI=-82' 45 | '-DRSSI_THRESHOLD=12' ; Apply a delta of 12 to average RSSI level 46 | ; '-DAVERAGE_RSSI=5000' ; Display RSSI floor ( Average of 5000 samples ) 47 | '-DSIGNAL_RSSI=true' ; Display during signal receive 48 | ; *** RF Module Options *** 49 | '-DRF_CC1101="CC1101"' ; CC1101 Transceiver Module 50 | ; '-DRF_MODULE_CS=5' ; pin to be used as chip select 51 | '-DRF_MODULE_GDO0=22' ; CC1101 pin GDO0 - Breadboard is 22 versus 13 on soldered 52 | '-DRF_MODULE_GDO2=4' ; CC1101 pin GDO2 53 | '-DRF_MODULE_INIT_STATUS=true' ; Display transceiver config during startup 54 | ; *** RadioLib Options *** 55 | '-DRADIOLIB_DEBUG=true' 56 | ; '-DRADIOLIB_VERBOSE=true' 57 | monitor_port = /dev/cu.SLAB_USBtoUART 58 | monitor_speed = 921600 59 | upload_port = /dev/cu.SLAB_USBtoUART 60 | upload_speed = 921600 61 | 62 | [env:esp32_heltec] 63 | monitor_filters = esp32_exception_decoder 64 | board = heltec_wifi_lora_32_V2 65 | build_type = debug 66 | build_flags = 67 | '-DLOG_LEVEL=LOG_LEVEL_TRACE' 68 | '-DONBOARD_LED=25' ; Onboard LED is GPIO 25 on the Heltec Board 69 | ; *** rtl_433_ESP Options *** 70 | ; '-DRTL_DEBUG=4' ; rtl_433 verbose mode 71 | ; '-DRTL_VERBOSE=58' ; LaCrosse TX141-Bv2, TX141TH-Bv2, TX141-Bv3, TX141W, TX145wsdth sensor 72 | ; '-DRAW_SIGNAL_DEBUG=true' ; display raw received messages 73 | ; '-DMEMORY_DEBUG=true' ; display memory usage information 74 | '-DDEMOD_DEBUG=true' ; display signal debug info 75 | ; '-DMY_DEVICES=true' ; subset of devices 76 | '-DPUBLISH_UNPARSED=true' ; publish unparsed signal details 77 | '-DRSSI_THRESHOLD=12' ; Apply a delta of 12 78 | '-DOOK_FIXED_THRESHOLD=0x50' ; Inital OOK Threhold - Only for SX127X 79 | ; '-DAVERAGE_RSSI=5000' ; Display RSSI floor ( Average of 5000 samples ) 80 | '-DSIGNAL_RSSI=true' ; Display during signal receive 81 | ; *** RF Module Options *** 82 | ; '-DRF_SX1278="SX1278"' ; Heltec ESP 32 Module - module settings come from heltec_wifi_lora_32_V2/pins_arduino.h 83 | ; '-DRF_MODULE_DIO0=26' ; SX1276 pin DIO0 84 | ; '-DRF_MODULE_DIO1=35' ; SX1276 pin DIO1 85 | ; '-DRF_MODULE_DIO2=34' ; SX1276 pin DIO2 86 | ; '-DRF_MODULE_RST=14' ; pin to be used as hardware reset 87 | '-DRF_MODULE_INIT_STATUS=true' ; Display transceiver config during startup 88 | ; *** Heltec module requires non-standard SPI Config *** 89 | ; '-DRF_MODULE_CS=18' ; pin to be used as chip select 90 | ; '-DRF_MODULE_MOSI=27' 91 | ; '-DRF_MODULE_MISO=19' 92 | ; '-DRF_MODULE_SCK=5' 93 | ; *** RadioLib Options *** 94 | '-DRADIOLIB_DEBUG=true' 95 | ; '-DRADIOLIB_VERBOSE=true' 96 | monitor_port = /dev/cu.SLAB_USBtoUART 97 | monitor_speed = 921600 98 | upload_port = /dev/cu.SLAB_USBtoUART 99 | upload_speed = 921600 100 | -------------------------------------------------------------------------------- /src/rtl_433/devices/oregon_scientific_v1.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | OSv1 protocol. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | */ 9 | /** 10 | OSv1 protocol. 11 | 12 | MC with nominal bit width of 2930 us. 13 | Pulses are somewhat longer than nominal half-bit width, 1748 us / 3216 us, 14 | Gaps are somewhat shorter than nominal half-bit width, 1176 us / 2640 us. 15 | After 12 preamble bits there is 4200 us gap, 5780 us pulse, 5200 us gap. 16 | 17 | Care must be taken with the gap after the sync pulse since it 18 | is outside of the normal clocking. Because of this a data stream 19 | beginning with a 0 will have data in this gap. 20 | 21 | */ 22 | 23 | #include "decoder.h" 24 | 25 | #define OSV1_BITS 32 26 | 27 | static int oregon_scientific_v1_callback(r_device *decoder, bitbuffer_t *bitbuffer) 28 | { 29 | int ret = 0; 30 | int row; 31 | int cs; 32 | int i; 33 | int nibble[OSV1_BITS/4]; 34 | int sid, channel; 35 | // int uk1; 36 | float tempC; 37 | int battery, sign, checksum; 38 | // int uk2, uk3; 39 | data_t *data; 40 | 41 | for (row = 0; row < bitbuffer->num_rows; row++) { 42 | if (bitbuffer->bits_per_row[row] != OSV1_BITS) 43 | continue; // DECODE_ABORT_LENGTH 44 | 45 | cs = 0; 46 | for (i = 0; i < OSV1_BITS / 8; i++) { 47 | uint8_t byte = reverse8(bitbuffer->bb[row][i]); 48 | nibble[i * 2 ] = byte & 0x0f; 49 | nibble[i * 2 + 1] = byte >> 4; 50 | if (i < ((OSV1_BITS / 8) - 1)) 51 | cs += nibble[i * 2] + 16 * nibble[i * 2 + 1]; 52 | } 53 | 54 | 55 | // No need to decode/extract values for simple test 56 | if ( bitbuffer->bb[row][0] == 0xFF && bitbuffer->bb[row][1] == 0xFF 57 | && bitbuffer->bb[row][2] == 0xFF && bitbuffer->bb[row][3] == 0xFF ) { 58 | if (decoder->verbose > 1) { 59 | fprintf(stderr, "%s: DECODE_FAIL_SANITY data all 0xff\n", __func__); 60 | } 61 | continue; // DECODE_FAIL_SANITY 62 | } 63 | 64 | cs = (cs & 0xFF) + (cs >> 8); 65 | checksum = nibble[6] + (nibble[7] << 4); 66 | /* reject 0x00 checksums to reduce false positives */ 67 | if (!checksum || (checksum != cs)) 68 | continue; // DECODE_FAIL_MIC 69 | 70 | sid = nibble[0]; 71 | channel = ((nibble[1] >> 2) & 0x03) + 1; 72 | //uk1 = (nibble[1] >> 0) & 0x03; /* unknown. Seen change every 60 minutes */ 73 | tempC = nibble[2] * 0.1 + nibble[3] + nibble[4] * 10.; 74 | battery = (nibble[5] >> 3) & 0x01; 75 | //uk2 = (nibble[5] >> 2) & 0x01; /* unknown. Always zero? */ 76 | sign = (nibble[5] >> 1) & 0x01; 77 | //uk3 = (nibble[5] >> 0) & 0x01; /* unknown. Always zero? */ 78 | 79 | if (sign) 80 | tempC = -tempC; 81 | 82 | /* clang-format off */ 83 | data = data_make( 84 | "model", "", DATA_STRING, "Oregon-v1", 85 | "id", "SID", DATA_INT, sid, 86 | "channel", "Channel", DATA_INT, channel, 87 | "battery_ok", "Battery", DATA_INT, !battery, 88 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, tempC, 89 | "mic", "Integrity", DATA_STRING, "CHECKSUM", 90 | NULL); 91 | /* clang-format on */ 92 | 93 | decoder_output_data(decoder, data); 94 | ret++; 95 | } 96 | return ret; 97 | } 98 | 99 | static char *output_fields[] = { 100 | "model", 101 | "id", 102 | "channel", 103 | "battery_ok", 104 | "temperature_C", 105 | "mic", 106 | NULL, 107 | }; 108 | 109 | r_device oregon_scientific_v1 = { 110 | .name = "OSv1 Temperature Sensor", 111 | .modulation = OOK_PULSE_PWM_OSV1, 112 | .short_width = 1465, // nominal half-bit width 113 | .sync_width = 5780, 114 | .gap_limit = 3500, 115 | .reset_limit = 14000, 116 | .decode_fn = &oregon_scientific_v1_callback, 117 | .disabled = 0, 118 | .fields = output_fields, 119 | }; 120 | -------------------------------------------------------------------------------- /src/rtl_433/devices/bresser_3ch.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Bresser sensor protocol. 3 | 4 | Copyright (C) 2015 Christian W. Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | /** 13 | Bresser sensor protocol. 14 | 15 | The protocol is for the wireless Temperature/Humidity sensor 16 | - Bresser Thermo-/Hygro-Sensor 3CH 17 | - also works for Renkforce DM-7511 18 | 19 | The sensor sends 15 identical packages of 40 bits each ~60s. 20 | The bits are PWM modulated with On Off Keying. 21 | 22 | A short pulse of 250 us followed by a 500 us gap is a 0 bit, 23 | a long pulse of 500 us followed by a 250 us gap is a 1 bit, 24 | there is a sync preamble of pulse, gap, 750 us each, repeated 4 times. 25 | Actual received and demodulated timings might be 2% shorter. 26 | 27 | The data is grouped in 5 bytes / 10 nibbles 28 | 29 | [id] [id] [flags] [temp] [temp] [temp] [humi] [humi] [chk] [chk] 30 | 31 | - id is an 8 bit random id that is generated when the sensor starts 32 | - flags are 4 bits battery low indicator, test button press and channel 33 | - temp is 12 bit unsigned fahrenheit offset by 90 and scaled by 10 34 | - humi is 8 bit relative humidity percentage 35 | - chk is the sum of the four data bytes 36 | */ 37 | #include "decoder.h" 38 | 39 | static int bresser_3ch_decode(r_device *decoder, bitbuffer_t *bitbuffer) 40 | { 41 | data_t *data; 42 | uint8_t *b; 43 | 44 | int id, battery_low, channel, temp_raw, humidity; 45 | // int status, test; 46 | float temp_f; 47 | 48 | int r = bitbuffer_find_repeated_row(bitbuffer, 3, 40); 49 | if (r < 0 || bitbuffer->bits_per_row[r] > 42) { 50 | return DECODE_ABORT_LENGTH; 51 | } 52 | 53 | b = bitbuffer->bb[r]; 54 | b[0] = ~b[0]; 55 | b[1] = ~b[1]; 56 | b[2] = ~b[2]; 57 | b[3] = ~b[3]; 58 | b[4] = ~b[4]; 59 | 60 | if (((b[0] + b[1] + b[2] + b[3] - b[4]) & 0xFF) != 0) { 61 | if (decoder->verbose) { 62 | fprintf(stderr, "%s: checksum error\n", __func__); 63 | } 64 | return DECODE_FAIL_MIC; 65 | } 66 | 67 | id = b[0]; 68 | //status = b[1]; 69 | battery_low = (b[1] & 0x80) >> 7; 70 | //test = (b[1] & 0x40) >> 6; 71 | channel = (b[1] & 0x30) >> 4; 72 | 73 | temp_raw = ((b[1] & 0x0F) << 8) + b[2]; 74 | // 12 bits allows for values -90.0 F - 319.6 F (-67 C - 159 C) 75 | temp_f = (temp_raw - 900) * 0.1f; 76 | 77 | humidity = b[3]; 78 | 79 | if ((channel == 0) || (humidity > 100) || (temp_f < -20.0) || (temp_f > 160.0)) { 80 | if (decoder->verbose) { 81 | fprintf(stderr, "%s: data error\n", __func__); 82 | } 83 | return DECODE_FAIL_SANITY; 84 | } 85 | 86 | /* clang-format off */ 87 | data = data_make( 88 | "model", "", DATA_STRING, "Bresser-3CH", 89 | "id", "Id", DATA_INT, id, 90 | "channel", "Channel", DATA_INT, channel, 91 | "battery_ok", "Battery", DATA_INT, !battery_low, 92 | "temperature_F", "Temperature", DATA_FORMAT, "%.2f F", DATA_DOUBLE, temp_f, 93 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, 94 | "mic", "Integrity", DATA_STRING, "CHECKSUM", 95 | NULL); 96 | /* clang-format on */ 97 | 98 | decoder_output_data(decoder, data); 99 | return 1; 100 | } 101 | 102 | static char *output_fields[] = { 103 | "model", 104 | "id", 105 | "channel", 106 | "battery_ok", 107 | "temperature_F", 108 | "humidity", 109 | "mic", 110 | NULL, 111 | }; 112 | 113 | r_device bresser_3ch = { 114 | .name = "Bresser Thermo-/Hygro-Sensor 3CH", 115 | .modulation = OOK_PULSE_PWM, 116 | .short_width = 250, // short pulse is ~250 us 117 | .long_width = 500, // long pulse is ~500 us 118 | .sync_width = 750, // sync pulse is ~750 us 119 | .gap_limit = 625, // long gap (with short pulse) is ~500 us, sync gap is ~750 us 120 | .reset_limit = 1250, // maximum gap is 1000 us (long gap + longer sync gap on last repeat) 121 | .decode_fn = &bresser_3ch_decode, 122 | .fields = output_fields, 123 | }; 124 | -------------------------------------------------------------------------------- /src/rtl_433/devices/cardin.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Cardin S466-TX2 generic garage door remote control on 27.195 Mhz. 3 | 4 | Copyright (C) 2015 Denis Bodor 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License version 2 as 8 | published by the Free Software Foundation. 9 | */ 10 | /** 11 | Cardin S466-TX2 generic garage door remote control on 27.195 Mhz. 12 | 13 | Remember to set de freq right with -f 27195000 14 | May be useful for other Cardin product too 15 | 16 | - "11R" = on-on Right button used 17 | - "10R" = on-off Right button used 18 | - "01R" = off-on Right button used 19 | - "00L?" = off-off Left button used or right button does the same as the left 20 | */ 21 | 22 | #include "decoder.h" 23 | 24 | static int cardin_callback(r_device *decoder, bitbuffer_t *bitbuffer) 25 | { 26 | uint8_t *b = bitbuffer->bb[0]; 27 | unsigned char dip[10] = {'-','-','-','-','-','-','-','-','-', '\0'}; 28 | 29 | char *rbutton[4] = { "11R", "10R", "01R", "00L?" }; 30 | 31 | if (bitbuffer->bits_per_row[0] != 24) 32 | return DECODE_ABORT_LENGTH; 33 | 34 | // validate message as we can 35 | if ((b[2] & 48) == 0 && ( 36 | (b[2] & 0x0f) == 3 || 37 | (b[2] & 0x0f) == 9 || 38 | (b[2] & 0x0f) == 12 || 39 | (b[2] & 0x0f) == 6) ) { 40 | 41 | /* 42 | fprintf(stderr, "------------------------------\n"); 43 | fprintf(stderr, "protocol = Cardin S466\n"); 44 | fprintf(stderr, "message = "); 45 | for (i=0 ; i<3 ; i++) { 46 | for (k = 7; k >= 0; k--) { 47 | if (b[i] & 1 << k) 48 | fprintf(stderr, "1"); 49 | else 50 | fprintf(stderr, "0"); 51 | } 52 | fprintf(stderr, " "); 53 | } 54 | fprintf(stderr, "\n\n"); 55 | */ 56 | 57 | // Dip 1 58 | if (b[0] & 8) { 59 | dip[0] = 'o'; 60 | if (b[1] & 8) 61 | dip[0] = '+'; 62 | } 63 | // Dip 2 64 | if (b[0] & 16) { 65 | dip[1] = 'o'; 66 | if (b[1] & 16) 67 | dip[1] = '+'; 68 | } 69 | // Dip 3 70 | if (b[0] & 32) { 71 | dip[2] = 'o'; 72 | if (b[1] & 32) 73 | dip[2] = '+'; 74 | } 75 | // Dip 4 76 | if (b[0] & 64) { 77 | dip[3] = 'o'; 78 | if (b[1] & 64) 79 | dip[3] = '+'; 80 | } 81 | // Dip 5 82 | if (b[0] & 128) { 83 | dip[4] = 'o'; 84 | if (b[1] & 128) 85 | dip[4] = '+'; 86 | } 87 | // Dip 6 88 | if (b[2] & 128) { 89 | dip[5] = 'o'; 90 | if (b[2] & 64) 91 | dip[5] = '+'; 92 | } 93 | // Dip 7 94 | if (b[0] & 1) { 95 | dip[6] = 'o'; 96 | if (b[1] & 1) 97 | dip[6] = '+'; 98 | } 99 | // Dip 8 100 | if (b[0] & 2) { 101 | dip[7] = 'o'; 102 | if (b[1] & 2) 103 | dip[7] = '+'; 104 | } 105 | // Dip 9 106 | if (b[0] & 4) { 107 | dip[8] = 'o'; 108 | if (b[1] & 4) 109 | dip[8] = '+'; 110 | } 111 | 112 | /* clang-format off */ 113 | data_t *data = data_make( 114 | "model", "", DATA_STRING, "Cardin-S466", 115 | "dipswitch", "dipswitch", DATA_STRING, dip, 116 | "rbutton", "right button switches", DATA_STRING, rbutton[((b[2] & 15) / 3)-1], 117 | NULL); 118 | /* clang-format on */ 119 | 120 | decoder_output_data(decoder, data); 121 | return 1; 122 | } 123 | return DECODE_ABORT_EARLY; 124 | } 125 | 126 | static char *output_fields[] = { 127 | "model", 128 | "dipswitch", 129 | "rbutton", 130 | NULL, 131 | }; 132 | 133 | r_device cardin = { 134 | .name = "Cardin S466-TX2", 135 | .modulation = OOK_PULSE_PWM, 136 | .short_width = 730, 137 | .long_width = 1400, 138 | .sync_width = 6150, 139 | .gap_limit = 1600, 140 | .reset_limit = 32000, 141 | .decode_fn = &cardin_callback, 142 | .disabled = 0, 143 | .fields = output_fields, 144 | }; 145 | -------------------------------------------------------------------------------- /src/rtl_433/devices/fs20.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Simple FS20 remote decoder. 3 | 4 | Copyright (C) 2019 Dominik Pusch 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License version 3 as 8 | published by the Free Software Foundation. 9 | */ 10 | 11 | /* 12 | Simple FS20 remote decoder. 13 | 14 | Frequency: use rtl_433 -f 868.35M 15 | 16 | fs20 protocol frame info from http://www.fhz4linux.info/tiki-index.php?page=FS20+Protocol 17 | 18 | preamble hc1 parity hc2 parity address parity cmd parity chksum parity eot 19 | 13 bit 8 bit 1 bit 8 bit 1 bit 8 bit 1 bit 8 bit 1 bit 8 bit 1 bit 1 bit 20 | 21 | with extended commands 22 | 23 | preamble hc1 parity hc2 parity address parity cmd parity ext parity chksum parity eot 24 | 13 bit 8 bit 1 bit 8 bit 1 bit 8 bit 1 bit 8 bit 1 bit 8 bit 1 bit 8 bit 1 bit 1 bit 25 | 26 | checksum and parity are not checked by this decoder. 27 | Command extensions are also not decoded. feel free to improve! 28 | */ 29 | 30 | #include "decoder.h" 31 | 32 | static int fs20_decode(r_device *decoder, bitbuffer_t *bitbuffer) 33 | { 34 | static char const *cmd_tab[] = { 35 | "off", 36 | "on, 6.25%", 37 | "on, 12.5%", 38 | "on, 18.75%", 39 | "on, 25%", 40 | "on, 31.25%", 41 | "on, 37.5%", 42 | "on, 43.75%", 43 | "on, 50%", 44 | "on, 56.25%", 45 | "on, 62.5%", 46 | "on, 68.75%", 47 | "on, 75%", 48 | "on, 81.25%", 49 | "on, 87.5%", 50 | "on, 93.75%", 51 | "on, 100%", 52 | "on, last value", 53 | "toggle on/off", 54 | "dim up", 55 | "dim down", 56 | "dim up/down", 57 | "set timer", 58 | "status request", 59 | "off, timer", 60 | "on, timer", 61 | "last value, timer", 62 | "reset to default", 63 | "unused", 64 | "unused", 65 | "unused", 66 | "unused", 67 | }; 68 | 69 | uint8_t *b; // bits of a row 70 | uint8_t cmd; 71 | uint8_t hc1; 72 | uint8_t hc2; 73 | uint16_t hc; 74 | uint8_t address; 75 | data_t *data; 76 | uint16_t ad_b4 = 0; 77 | uint32_t hc_b4 = 0; 78 | 79 | // check length of frame 80 | if (bitbuffer->bits_per_row[0] != 58) { 81 | // check extended length (never tested!) 82 | if (bitbuffer->bits_per_row[0] != 67) 83 | return DECODE_ABORT_LENGTH; 84 | } 85 | 86 | b = bitbuffer->bb[0]; 87 | // check preamble first 13 bits '0000 0000 0000 1' 88 | if ((b[0] != 0x00) || ((b[1] & 0xf8) != 0x08)) 89 | return DECODE_FAIL_SANITY; 90 | 91 | // parse values from buffer 92 | hc1 = (b[1] << 5) | (b[2] >> 3); 93 | hc2 = (b[2] << 6) | (b[3] >> 2); 94 | hc = hc1 << 8 | hc2; 95 | address = (b[3] << 7) | (b[4] >> 1); 96 | cmd = b[5] & 0x1f; 97 | 98 | // convert address to fs20 format (base4+1) 99 | for (uint8_t i = 0; i < 4; i++) { 100 | ad_b4 += (address % 4 + 1) << i * 4; 101 | address /= 4; 102 | } 103 | 104 | // convert housecode to fs20 format (base4+1) 105 | for (uint8_t i = 0; i < 8; i++) { 106 | hc_b4 += ((hc % 4) + 1) << i * 4; 107 | hc /= 4; 108 | } 109 | 110 | /* clang-format off */ 111 | data = data_make( 112 | "model", "", DATA_STRING, "FS20", 113 | "housecode", "", DATA_FORMAT, "%x", DATA_INT, hc_b4, 114 | "address", "", DATA_FORMAT, "%x", DATA_INT, ad_b4, 115 | "command", "", DATA_STRING, cmd_tab[cmd], 116 | NULL); 117 | /* clang-format on */ 118 | decoder_output_data(decoder, data); 119 | 120 | return 1; 121 | } 122 | 123 | static char *output_fields[] = { 124 | "model", 125 | "housecode", 126 | "address", 127 | "command", 128 | NULL, 129 | }; 130 | 131 | r_device fs20 = { 132 | .name = "FS20", 133 | .modulation = OOK_PULSE_PPM, 134 | .short_width = 400, 135 | .long_width = 600, 136 | .reset_limit = 9000, 137 | .decode_fn = &fs20_decode, 138 | .disabled = 1, // missing MIC and no sample data 139 | .fields = output_fields, 140 | }; 141 | -------------------------------------------------------------------------------- /src/rtl_433/devices/ts_ft002.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | TS-FT002 Tank Liquid Level decoder. 3 | 4 | Copyright (C) 2019 Christian W. Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /* 12 | TS-FT002 Wireless Ultrasonic Tank Liquid Level Meter With Temperature Sensor 13 | 14 | PPM with 500 us pulse, 464 us short gap (0), 948 us long gap (1), 1876 us packet gap, two packets per transmission. 15 | 16 | E.g. rtl_433 -R 0 -X 'n=TS-FT002T,m=OOK_PPM,s=500,l=1000,g=1200,r=2000,bits>=70' 17 | 18 | Bits are sent LSB first, full packet is 9 bytes (1 byte preamble + 8 bytes payload) 19 | 20 | Data layout: 21 | 22 | SS II MM DD BD VT TT CC 23 | 24 | (Nibble number after reflecting bytes) 25 | | Nibble | Description 26 | | 0,1 | Sync 0xfa (0x5f before reverse) 27 | | 2,3 | ID 28 | | 4,5 | Message type (fixed 0x11) 29 | | 6,7,9 | Depth H,M,L (in Centimeter, 0x5DC if invalid, range 0-15M) 30 | | 8 | Transmit Interval (bit 7=0: 180S, bit 7 =1: 30S, bit 4-6=1: 5S) 31 | | 10 | Battery indicator? 32 | | 12,13,11 | Temp H, M, L (scale 10, offset 400), 0x3E8 if invalid 33 | | 14,15 | Rain H, L (Value 0-256), not used 34 | | 16,17 | XOR checksum (include the preamble) 35 | */ 36 | 37 | #include "decoder.h" 38 | 39 | static int ts_ft002_decoder(r_device *decoder, bitbuffer_t *bitbuffer) 40 | { 41 | data_t *data; 42 | uint8_t b[9]; 43 | int id, type, depth, transmit, temp_raw, batt_low; 44 | float temp_c; 45 | 46 | if (bitbuffer->bits_per_row[0] == 72) { 47 | bitbuffer_extract_bytes(bitbuffer, 0, 0, b, 72); 48 | } 49 | else if (bitbuffer->bits_per_row[0] == 71) { 50 | bitbuffer_extract_bytes(bitbuffer, 0, 7, b + 1, 64); 51 | b[0] = bitbuffer->bb[0][0] >> 1; 52 | } 53 | else if (bitbuffer->bits_per_row[0] == 70) { 54 | bitbuffer_extract_bytes(bitbuffer, 0, 6, b + 1, 64); 55 | b[0] = (bitbuffer->bb[0][0] >> 2) | 0x80; 56 | } 57 | else 58 | return DECODE_ABORT_LENGTH; 59 | 60 | int chk = xor_bytes(b, 9); 61 | if (chk) 62 | return DECODE_FAIL_MIC; 63 | 64 | // reflect bits (also reverses nibbles) 65 | reflect_bytes(b, 8); 66 | 67 | id = b[1]; 68 | type = b[2]; 69 | depth = (b[3] << 4) | (b[4] & 0x0f); 70 | batt_low = b[4] >> 4; 71 | transmit = b[5] >> 4; 72 | temp_raw = (b[6] << 4) | (b[5] & 0x0f); 73 | //rain = b[7]; 74 | //chk = b[8]; 75 | temp_c = (temp_raw - 400) * 0.1f; 76 | 77 | if ((transmit & 0x07) == 0x07) 78 | transmit = 5; 79 | else if ((transmit & 0x08) == 0x08) 80 | transmit = 30; 81 | else if (transmit == 0) 82 | transmit = 180; 83 | else // invalid 84 | transmit = 0; 85 | 86 | if (type != 0x11) 87 | return DECODE_FAIL_SANITY; 88 | 89 | /* clang-format off */ 90 | data = data_make( 91 | "model", "", DATA_STRING, "TS-FT002", 92 | "id", "Id", DATA_INT, id, 93 | "depth_cm", "Depth", DATA_INT, depth, 94 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, 95 | "transmit_s", "Transmit Interval", DATA_INT, transmit, 96 | //"battery_ok", "Battery", DATA_INT, batt_low, 97 | "flags", "Battery Flag?", DATA_INT, batt_low, 98 | "mic", "Integrity", DATA_STRING, "CHECKSUM", 99 | NULL); 100 | decoder_output_data(decoder, data); 101 | /* clang-format on */ 102 | 103 | return 1; 104 | } 105 | 106 | static char *output_fields[] = { 107 | "model", 108 | "id", 109 | "depth_cm", 110 | "temperature_C", 111 | "transmit_s", 112 | //"battery_ok", 113 | "flags", 114 | "mic", 115 | NULL, 116 | }; 117 | 118 | r_device ts_ft002 = { 119 | .name = "TS-FT002 Wireless Ultrasonic Tank Liquid Level Meter With Temperature Sensor", 120 | .modulation = OOK_PULSE_PPM, 121 | .short_width = 464, 122 | .long_width = 948, 123 | .gap_limit = 1200, 124 | .reset_limit = 2000, 125 | .decode_fn = &ts_ft002_decoder, 126 | .disabled = 0, 127 | .fields = output_fields, 128 | }; 129 | -------------------------------------------------------------------------------- /include/decoder_util.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | High-level utility functions for decoders. 3 | 4 | Copyright (C) 2018 Christian Zuckschwerdt 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | 12 | #ifndef INCLUDE_DECODER_UTIL_H_ 13 | #define INCLUDE_DECODER_UTIL_H_ 14 | 15 | #include 16 | #include "bitbuffer.h" 17 | #include "data.h" 18 | #include "r_device.h" 19 | 20 | #if defined _MSC_VER || defined ESP32 // Microsoft Visual Studio or ESP32 21 | // MSC and ESP32 have something like C99 restrict as __restrict 22 | #ifndef restrict 23 | #define restrict __restrict 24 | #endif 25 | #endif 26 | // Defined in newer for MSVC. 27 | #ifndef _Printf_format_string_ 28 | #define _Printf_format_string_ 29 | #endif 30 | 31 | /// Create a new r_device, copy from dev_template if not NULL. 32 | r_device *create_device(r_device *dev_template); 33 | 34 | /// Output data. 35 | void decoder_output_data(r_device *decoder, data_t *data); 36 | 37 | // be terse, a maximum msg length of 60 characters is supported on the decoder_output_ functions 38 | // e.g. "FoobarCorp-XY3000: unexpected type code %02x" 39 | 40 | /// Output a message. 41 | void decoder_output_message(r_device *decoder, char const *msg); 42 | 43 | /// Output a message and the content of a bitbuffer. 44 | void decoder_output_bitbuffer(r_device *decoder, bitbuffer_t const *bitbuffer, char const *msg); 45 | 46 | /// Output a message and the content of a bitbuffer. 47 | /// Usage not recommended. 48 | void decoder_output_bitbuffer_array(r_device *decoder, bitbuffer_t const *bitbuffer, char const *msg); 49 | 50 | /// Output a message and the content of a bit row (byte buffer). 51 | void decoder_output_bitrow(r_device *decoder, uint8_t const *bitrow, unsigned bit_len, char const *msg); 52 | 53 | // print helpers 54 | 55 | /// Output a message with args. 56 | void decoder_output_messagef(r_device *decoder, _Printf_format_string_ char const *restrict format, ...) 57 | #if defined(__GNUC__) || defined(__clang__) 58 | __attribute__((format(printf, 2, 3))) 59 | #endif 60 | ; 61 | 62 | /// Output a message with args and the content of a bitbuffer. 63 | void decoder_output_bitbufferf(r_device *decoder, bitbuffer_t const *bitbuffer, _Printf_format_string_ char const *restrict format, ...) 64 | #if defined(__GNUC__) || defined(__clang__) 65 | __attribute__((format(printf, 3, 4))) 66 | #endif 67 | ; 68 | 69 | /// Output a message with args and the content of a bitbuffer. 70 | void decoder_output_bitbuffer_arrayf(r_device *decoder, bitbuffer_t const *bitbuffer, _Printf_format_string_ char const *restrict format, ...) 71 | #if defined(__GNUC__) || defined(__clang__) 72 | __attribute__((format(printf, 3, 4))) 73 | #endif 74 | ; 75 | 76 | /// Output a message with args and the content of a bit row (byte buffer). 77 | void decoder_output_bitrowf(r_device *decoder, uint8_t const *bitrow, unsigned bit_len, _Printf_format_string_ char const *restrict format, ...) 78 | #if defined(__GNUC__) || defined(__clang__) 79 | __attribute__((format(printf, 4, 5))) 80 | #endif 81 | ; 82 | 83 | /// Print the content of the bitbuffer. 84 | void bitbuffer_printf(const bitbuffer_t *bitbuffer, _Printf_format_string_ char const *restrict format, ...) 85 | #if defined(__GNUC__) || defined(__clang__) 86 | __attribute__((format(printf, 2, 3))) 87 | #endif 88 | ; 89 | 90 | /// Debug print the content of the bitbuffer. 91 | /// For quick and easy debugging, not for regular usage. 92 | void bitbuffer_debugf(const bitbuffer_t *bitbuffer, _Printf_format_string_ char const *restrict format, ...) 93 | #if defined(__GNUC__) || defined(__clang__) 94 | __attribute__((format(printf, 2, 3))) 95 | #endif 96 | ; 97 | 98 | /// Print the content of a bit row (byte buffer). 99 | void bitrow_printf(uint8_t const *bitrow, unsigned bit_len, _Printf_format_string_ char const *restrict format, ...) 100 | #if defined(__GNUC__) || defined(__clang__) 101 | __attribute__((format(printf, 3, 4))) 102 | #endif 103 | ; 104 | 105 | /// Debug print the content of a bit row (byte buffer). 106 | /// For quick and easy debugging, not for regular usage. 107 | void bitrow_debugf(uint8_t const *bitrow, unsigned bit_len, _Printf_format_string_ char const *restrict format, ...) 108 | #if defined(__GNUC__) || defined(__clang__) 109 | __attribute__((format(printf, 3, 4))) 110 | #endif 111 | ; 112 | 113 | #endif /* INCLUDE_DECODER_UTIL_H_ */ 114 | -------------------------------------------------------------------------------- /src/rtl_433/devices/hcs200.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | Microchip HCS200 KeeLoq Code Hopping Encoder based remotes. 3 | 4 | Copyright (C) 2019, 667bdrm 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | */ 11 | /** 12 | Microchip HCS200 KeeLoq Code Hopping Encoder based remotes. 13 | 14 | 66 bits transmitted, LSB first 15 | 16 | | 0-31 | Encrypted Portion 17 | | 32-59 | Serial Number 18 | | 60-63 | Button Status 19 | | 64 | Battery Low 20 | | 65 | Fixed 1 21 | 22 | Datasheet: DS40138C http://ww1.microchip.com/downloads/en/DeviceDoc/40138c.pdf 23 | 24 | The warmup of 12 short pulses is followed by a long 4400 us gap. 25 | There are two packets with a 17500 us gap. 26 | 27 | rtl_433 -R 0 -X 'n=hcs200,m=OOK_PWM,s=370,l=772,r=9000,g=1500,t=152' 28 | */ 29 | 30 | #include "decoder.h" 31 | 32 | static int hcs200_callback(r_device *decoder, bitbuffer_t *bitbuffer) 33 | { 34 | data_t *data; 35 | uint8_t *b = bitbuffer->bb[0]; 36 | uint32_t encrypted, serial, encrypted_rev, serial_rev; 37 | char encrypted_str[9]; 38 | char encrypted_rev_str[9]; 39 | char serial_str[9]; 40 | char serial_rev_str[9]; 41 | 42 | // Reject codes of wrong length 43 | if (bitbuffer->bits_per_row[0] != 12 || bitbuffer->bits_per_row[1] != 66) 44 | return DECODE_ABORT_LENGTH; 45 | 46 | // Reject codes with an incorrect preamble (expected 0xfff) 47 | if (b[0] != 0xff || (b[1] & 0xf0) != 0xf0) { 48 | if (decoder->verbose > 1) 49 | fprintf(stderr, "%s: Preamble not found\n", __func__); 50 | return DECODE_ABORT_EARLY; 51 | } 52 | 53 | // Second row is data 54 | b = bitbuffer->bb[1]; 55 | 56 | // No need to decode/extract values for simple test 57 | if (b[1] == 0xff && b[2] == 0xff && b[3] == 0xff && b[4] == 0xff 58 | && b[5] == 0xff && b[6] == 0xff && b[7] == 0xff) { 59 | if (decoder->verbose > 1) { 60 | fprintf(stderr, "%s: DECODE_FAIL_SANITY data all 0xff\n", __func__); 61 | } 62 | return DECODE_FAIL_SANITY; 63 | } 64 | 65 | encrypted = ((unsigned)b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]); 66 | serial = (b[4] << 20) | (b[5] << 12) | (b[6] << 4) | (b[7] >> 4); 67 | encrypted_rev = reverse32(encrypted); 68 | serial_rev = reverse32(serial); 69 | 70 | sprintf(encrypted_str, "%08X", encrypted); 71 | sprintf(serial_str, "%08X", serial); 72 | sprintf(encrypted_rev_str, "%08X", encrypted_rev); 73 | sprintf(serial_rev_str, "%08X", serial_rev); 74 | 75 | /* clang-format off */ 76 | data = data_make( 77 | "model", "", DATA_STRING, "Microchip-HCS200", 78 | "id", "", DATA_STRING, serial_str, 79 | "id_rev", "", DATA_STRING, serial_rev_str, 80 | "encrypted", "", DATA_STRING, encrypted_str, 81 | "encrypted_rev", "", DATA_STRING, encrypted_rev_str, 82 | "button1", "", DATA_STRING, ((b[7] & 0x04) == 0x04) ? "ON" : "OFF", 83 | "button2", "", DATA_STRING, ((b[7] & 0x02) == 0x02) ? "ON" : "OFF", 84 | "button3", "", DATA_STRING, ((b[7] & 0x09) == 0x09) ? "ON" : "OFF", 85 | "button4", "", DATA_STRING, ((b[7] & 0x06) == 0x06) ? "ON" : "OFF", 86 | "misc", "", DATA_STRING, (b[7] == 0x0F) ? "ALL_PRESSED" : "", 87 | "battery_ok", "Battery", DATA_INT, (((b[8] >> 4) & 0x08) == 0x08) ? 0 : 1, 88 | NULL); 89 | /* clang-format on */ 90 | 91 | decoder_output_data(decoder, data); 92 | return 1; 93 | } 94 | 95 | static char *output_fields[] = { 96 | "model", 97 | "id", 98 | "id_rev", 99 | "encrypted", 100 | "encrypted_rev", 101 | "button1", 102 | "button2", 103 | "button3", 104 | "button4", 105 | "misc", 106 | "battery_ok", 107 | NULL, 108 | }; 109 | 110 | r_device hcs200 = { 111 | .name = "Microchip HCS200 KeeLoq Hopping Encoder based remotes", 112 | .modulation = OOK_PULSE_PWM, 113 | .short_width = 370, 114 | .long_width = 772, 115 | .gap_limit = 1500, 116 | .reset_limit = 9000, 117 | .tolerance = 152, // us 118 | .decode_fn = &hcs200_callback, 119 | .fields = output_fields, 120 | }; 121 | --------------------------------------------------------------------------------