Merge pull request #74 from helgew/refactor

Refactoring of device support and combined pull requests for PRs #34, #50, #52, #54, #57, and #60
This commit is contained in:
Benjamin Larsson
2015-01-17 10:32:45 +01:00
19 changed files with 1930 additions and 1127 deletions

8
.gitignore vendored
View File

@@ -24,6 +24,7 @@ install-sh
stamp-h1
libtool
Doxyfile
build
.tarball-version
.version
@@ -39,3 +40,10 @@ CMakeCache.txt
*/CMakeFiles
CMakeFiles
*.cmake
build/
.cproject
.settings
.project
*.orig

17
README
View File

@@ -29,3 +29,20 @@ Usage: [-d device_index (default: 0)]
./rtl_433 will run the software in receive mode. Some sensor data can be receviced.
This software is mostly useable for developers right now.
## Supported Devices
Rubicson Temperature Sensor
Silvercrest Remote Control
ELV EM 1000
ELV WS 2000
Waveman Switch Transmitter
Steffen Switch Transmitter
Acurite 5n1 Weather Station
Acurite Temperature and Humidity Sensor
Acurite 896 Rain Gauge
LaCrosse TX Temperature / Humidity Sensor
Oregon Scientific Weather Sensor
KlikAanKlikUit Wireless Switch
AlectoV1 Weather Sensor
Intertechno 433
Mebus 433

54
include/rtl_433.h Normal file
View File

@@ -0,0 +1,54 @@
#ifndef INCLUDE_RTL_433_H_
#define INCLUDE_RTL_433_H_
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#ifndef _WIN32
#include <unistd.h>
#else
#include <Windows.h>
#include <io.h>
#include <fcntl.h>
#include "getopt/getopt.h"
#endif
#define DEFAULT_SAMPLE_RATE 250000
#define DEFAULT_FREQUENCY 433920000
#define DEFAULT_HOP_TIME (60*10)
#define DEFAULT_HOP_EVENTS 2
#define DEFAULT_ASYNC_BUF_NUMBER 32
#define DEFAULT_BUF_LENGTH (16 * 16384)
#define DEFAULT_LEVEL_LIMIT 10000
#define DEFAULT_DECIMATION_LEVEL 0
#define MINIMAL_BUF_LENGTH 512
#define MAXIMAL_BUF_LENGTH (256 * 16384)
#define FILTER_ORDER 1
#define MAX_PROTOCOLS 15
#define SIGNAL_GRABBER_BUFFER (12 * DEFAULT_BUF_LENGTH)
#define BITBUF_COLS 34
#define BITBUF_ROWS 50
/* Supported modulation types */
#define OOK_PWM_D 1 /* Pulses are of the same length, the distance varies */
#define OOK_PWM_P 2 /* The length of the pulses varies */
#define OOK_MANCHESTER 3 /* Manchester code */
typedef struct {
unsigned int id;
char name[256];
unsigned int modulation;
unsigned int short_limit;
unsigned int long_limit;
unsigned int reset_limit;
int (*json_callback)(uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) ;
} r_device;
extern int debug_output;
int debug_callback(uint8_t buffer[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]);
#endif /* INCLUDE_RTL_433_H_ */

21
include/rtl_433_devices.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef INCLUDE_RTL_433_DEVICES_H_
#define INCLUDE_RTL_433_DEVICES_H_
extern r_device silvercrest;
extern r_device rubicson;
extern r_device prologue;
extern r_device waveman;
extern r_device steffen;
extern r_device elv_em1000;
extern r_device elv_ws2000;
extern r_device lacrossetx;
extern r_device acurite5n1;
extern r_device acurite_rain_gauge;
extern r_device acurite_th;
extern r_device oregon_scientific;
extern r_device mebus433;
extern r_device intertechno;
extern r_device newkaku;
extern r_device alectov1;
#endif /* INCLUDE_RTL_433_DEVICES_H_ */

View File

@@ -72,7 +72,22 @@ add_executable(rtl_test rtl_test.c)
add_executable(rtl_fm rtl_fm.c)
add_executable(rtl_eeprom rtl_eeprom.c)
add_executable(rtl_adsb rtl_adsb.c)
add_executable(rtl_433 rtl_433.c)
add_executable(rtl_433
rtl_433.c
devices/silvercrest.c
devices/rubicson.c
devices/prologue.c
devices/waveman.c
devices/steffen.c
devices/elv.c
devices/lacrosse.c
devices/acurite.c
devices/oregon_scientific.c
devices/mebus.c
devices/intertechno.c
devices/alecto.c
devices/newkaku.c)
set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_test rtl_fm rtl_eeprom rtl_adsb rtl_433)
target_link_libraries(rtl_sdr rtlsdr_shared

205
src/devices/acurite.c Normal file
View File

@@ -0,0 +1,205 @@
#include "rtl_433.h"
// ** Acurite 5n1 functions **
const float acurite_winddirections[] =
{ 337.5, 315.0, 292.5, 270.0, 247.5, 225.0, 202.5, 180,
157.5, 135.0, 112.5, 90.0, 67.5, 45.0, 22.5, 0.0 };
static int acurite_raincounter = 0;
static int acurite_crc(uint8_t row[BITBUF_COLS], int cols) {
// sum of first n-1 bytes modulo 256 should equal nth byte
int i;
int sum = 0;
for ( i=0; i < cols; i++)
sum += row[i];
if ( sum % 256 == row[cols] )
return 1;
else
return 0;
}
static int acurite_detect(uint8_t *pRow) {
int i;
if ( pRow[0] != 0x00 ) {
// invert bits due to wierd issue
for (i = 0; i < 8; i++)
pRow[i] = ~pRow[i] & 0xFF;
pRow[0] |= pRow[8]; // fix first byte that has mashed leading bit
if (acurite_crc(pRow, 7))
return 1; // passes crc check
}
return 0;
}
static float acurite_getTemp (uint8_t highbyte, uint8_t lowbyte) {
// range -40 to 158 F
int highbits = (highbyte & 0x0F) << 7 ;
int lowbits = lowbyte & 0x7F;
int rawtemp = highbits | lowbits;
float temp = (rawtemp - 400) / 10.0;
return temp;
}
static int acurite_getWindSpeed (uint8_t highbyte, uint8_t lowbyte) {
// range: 0 to 159 kph
// TODO: sensor does not seem to be in kph, e.g.,
// a value of 49 here was registered as 41 kph on base unit
// value could be rpm, etc which may need (polynomial) scaling factor??
int highbits = ( highbyte & 0x1F) << 3;
int lowbits = ( lowbyte & 0x70 ) >> 4;
int speed = highbits | lowbits;
return speed;
}
static float acurite_getWindDirection (uint8_t byte) {
// 16 compass points, ccw from (NNW) to 15 (N)
int direction = byte & 0x0F;
return acurite_winddirections[direction];
}
static int acurite_getHumidity (uint8_t byte) {
// range: 1 to 99 %RH
int humidity = byte & 0x7F;
return humidity;
}
static int acurite_getRainfallCounter (uint8_t hibyte, uint8_t lobyte) {
// range: 0 to 99.99 in, 0.01 in incr., rolling counter?
int raincounter = ((hibyte & 0x7f) << 7) | (lobyte & 0x7F);
return raincounter;
}
static int acurite5n1_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) {
// acurite 5n1 weather sensor decoding for rtl_433
// Jens Jensen 2014
int i;
uint8_t *buf = NULL;
// run through rows til we find one with good crc (brute force)
for (i=0; i < BITBUF_ROWS; i++) {
if (acurite_detect(bb[i])) {
buf = bb[i];
break; // done
}
}
if (buf) {
// decode packet here
fprintf(stderr, "Detected Acurite 5n1 sensor, %d bits\n",bits_per_row[1]);
if (debug_output) {
for (i=0; i < 8; i++)
fprintf(stderr, "%02X ", buf[i]);
fprintf(stderr, "CRC OK\n");
}
if ((buf[2] & 0x0F) == 1) {
// wind speed, wind direction, rainfall
float rainfall = 0.00;
int raincounter = acurite_getRainfallCounter(buf[5], buf[6]);
if (acurite_raincounter > 0) {
// track rainfall difference after first run
rainfall = ( raincounter - acurite_raincounter ) * 0.01;
} else {
// capture starting counter
acurite_raincounter = raincounter;
}
fprintf(stderr, "wind speed: %d kph, ",
acurite_getWindSpeed(buf[3], buf[4]));
fprintf(stderr, "wind direction: %0.1f°, ",
acurite_getWindDirection(buf[4]));
fprintf(stderr, "rain gauge: %0.2f in.\n", rainfall);
} else if ((buf[2] & 0x0F) == 8) {
// wind speed, temp, RH
fprintf(stderr, "wind speed: %d kph, ",
acurite_getWindSpeed(buf[3], buf[4]));
fprintf(stderr, "temp: %2.1f° F, ",
acurite_getTemp(buf[4], buf[5]));
fprintf(stderr, "humidity: %d%% RH\n",
acurite_getHumidity(buf[6]));
}
} else {
return 0;
}
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
static int acurite_rain_gauge_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
// This needs more validation to positively identify correct sensor type, but it basically works if message is really from acurite raingauge and it doesn't have any errors
if ((bb[0][0] != 0) && (bb[0][1] != 0) && (bb[0][2]!=0) && (bb[0][3] == 0) && (bb[0][4] == 0)) {
float total_rain = ((bb[0][1]&0xf)<<8)+ bb[0][2];
total_rain /= 2; // Sensor reports number of bucket tips. Each bucket tip is .5mm
fprintf(stderr, "AcuRite Rain Gauge Total Rain is %2.1fmm\n", total_rain);
fprintf(stderr, "Raw Message: %02x %02x %02x %02x %02x\n",bb[0][0],bb[0][1],bb[0][2],bb[0][3],bb[0][4]);
return 1;
}
return 0;
}
static int acurite_th_detect(uint8_t *buf){
if(buf[5] != 0) return 0;
uint8_t sum = (buf[0] + buf[1] + buf[2] + buf[3]) & 0xff;
if(sum == 0) return 0;
return sum == buf[4];
}
static float acurite_th_temperature(uint8_t *s){
uint16_t shifted = (((s[1] & 0x0f) << 8) | s[2]) << 4; // Logical left shift
return (((int16_t)shifted) >> 4) / 10.0; // Arithmetic right shift
}
static int acurite_th_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
uint8_t *buf = NULL;
int i;
for(i = 0; i < BITBUF_ROWS; i++){
if(acurite_th_detect(bb[i])){
buf = bb[i];
break;
}
}
if(buf){
fprintf(stderr, "Temperature event:\n");
fprintf(stderr, "protocol = Acurite Temp&Humidity\n");
fprintf(stderr, "temp = %.1f°C\n", acurite_th_temperature(buf));
fprintf(stderr, "humidity = %d%%\n\n", buf[3]);
return 1;
}
return 0;
}
r_device acurite5n1 = {
/* .id = */ 10,
/* .name = */ "Acurite 5n1 Weather Station",
/* .modulation = */ OOK_PWM_P,
/* .short_limit = */ 70,
/* .long_limit = */ 240,
/* .reset_limit = */ 21000,
/* .json_callback = */ &acurite5n1_callback,
};
r_device acurite_rain_gauge = {
/* .id = */ 10,
/* .name = */ "Acurite 896 Rain Gauge",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 1744/4,
/* .long_limit = */ 3500/4,
/* .reset_limit = */ 5000/4,
/* .json_callback = */ &acurite_rain_gauge_callback,
};
r_device acurite_th = {
/* .id = */ 11,
/* .name = */ "Acurite Temperature and Humidity Sensor",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 300,
/* .long_limit = */ 550,
/* .reset_limit = */ 2500,
/* .json_callback = */ &acurite_th_callback,
};

163
src/devices/alecto.c Normal file
View File

@@ -0,0 +1,163 @@
#include "rtl_433.h"
/* Documentation also at http://www.tfd.hu/tfdhu/files/wsprotocol/auriol_protocol_v20.pdf
* Message Format: (9 nibbles, 36 bits):
* Please note that bytes need to be reversed before processing!
*
* Format for Temperature Humidity
* AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE
* RC Type Temperature___ Humidity Checksum
* A = Rolling Code / Device ID
* Device ID: AAAABBAA BB is used for channel, base channel is 01
* When channel selector is used, channel can be 10 (2) and 11 (3)
* B = Message type (xyyz = temp/humidity if yy <> '11') else wind/rain sensor
* x indicates battery status (0 normal, 1 voltage is below ~2.6 V)
* z 0 indicates regular transmission, 1 indicates requested by pushbutton
* C = Temperature (two's complement)
* D = Humidity BCD format
* E = Checksum
*
* Format for Rain
* AAAAAAAA BBBB CCCC DDDD DDDD DDDD DDDD EEEE
* RC Type Rain Checksum
* A = Rolling Code /Device ID
* B = Message type (xyyx = NON temp/humidity data if yy = '11')
* C = fixed to 1100
* D = Rain (bitvalue * 0.25 mm)
* E = Checksum
*
* Format for Windspeed
* AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE
* RC Type Windspd Checksum
* A = Rolling Code
* B = Message type (xyyx = NON temp/humidity data if yy = '11')
* C = Fixed to 1000 0000 0000
* D = Windspeed (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72)
* E = Checksum
*
* Format for Winddirection & Windgust
* AAAAAAAA BBBB CCCD DDDD DDDD EEEEEEEE FFFF
* RC Type Winddir Windgust Checksum
* A = Rolling Code
* B = Message type (xyyx = NON temp/humidity data if yy = '11')
* C = Fixed to 111
* D = Wind direction
* E = Windgust (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72)
* F = Checksum
*********************************************************************************************
*/
uint8_t reverse8(uint8_t x) {
x = (x & 0xF0) >> 4 | (x & 0x0F) << 4;
x = (x & 0xCC) >> 2 | (x & 0x33) << 2;
x = (x & 0xAA) >> 1 | (x & 0x55) << 1;
return x;
}
uint8_t bcd_decode8(uint8_t x) {
return ((x & 0xF0) >> 4) * 10 + (x & 0x0F);
}
static int alectov1_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
int temperature_before_dec;
int temperature_after_dec;
int16_t temp;
uint8_t humidity, csum = 0, csum2 = 0;
int i;
if (bb[1][0] == bb[5][0] && bb[2][0] == bb[6][0] && (bb[1][4] & 0xf) == 0 && (bb[5][4] & 0xf) == 0
&& (bb[5][0] != 0 && bb[5][1] != 0)) {
for (i = 0; i < 4; i++) {
uint8_t tmp = reverse8(bb[1][i]);
csum += (tmp & 0xf) + ((tmp & 0xf0) >> 4);
tmp = reverse8(bb[5][i]);
csum2 += (tmp & 0xf) + ((tmp & 0xf0) >> 4);
}
csum = ((bb[1][1] & 0x7f) == 0x6c) ? (csum + 0x7) : (0xf - csum);
csum2 = ((bb[5][1] & 0x7f) == 0x6c) ? (csum2 + 0x7) : (0xf - csum2);
csum = reverse8((csum & 0xf) << 4);
csum2 = reverse8((csum2 & 0xf) << 4);
/* Quit if checksup does not work out */
if (csum != (bb[1][4] >> 4) || csum2 != (bb[5][4] >> 4)) {
fprintf(stderr, "\nAlectoV1 CRC error");
return 0;
} //Invalid checksum
uint8_t wind = 0;
if ((bb[1][1] & 0xe0) == 0x60) {
wind = ((bb[1][1] & 0xf) == 0xc) ? 0 : 1;
fprintf(stderr, "\nSensor = %s event\n", wind ? "Wind" : "Rain gauge");
fprintf(stderr, "Protocol = AlectoV1 bpr1: %d bpr2: %d\n", bits_per_row[1], bits_per_row[5]);
fprintf(stderr, "Device = %d\n", reverse8(bb[1][0]));
fprintf(stderr, "Button = %d\n", bb[1][1]&0x10 ? 1 : 0);
fprintf(stderr, "Battery = %s\n", bb[1][1]&0x80 ? "Low" : "OK");
if (wind) {
int skip = -1;
/* Untested code written according to the specification, may not decode correctly */
if ((bb[1][1]&0xe) == 0x8 && bb[1][2] == 0) {
skip = 0;
} else if ((bb[1][1]&0xe) == 0xe) {
skip = 4;
} //According to supplied data!
if (skip >= 0) {
double speed = reverse8(bb[1 + skip][3]);
double gust = reverse8(bb[5 + skip][3]);
int direction = (reverse8(bb[5 + skip][2]) << 1) | (bb[5 + skip][1] & 0x1);
fprintf(stderr, "Wind speed = %.0f units = %.2f m/s\n", speed, speed * 0.2);
fprintf(stderr, "Wind gust = %.0f units = %.2f m/s\n", gust, gust * 0.2);
fprintf(stderr, "Direction = %.2i degrees\n", direction);
}
} else {
/* Untested code written according to the specification, may not decode correctly */
double rain_mm = (reverse8(bb[1][2]) + (reverse8(bb[1][3] << 8))) * 0.25;
fprintf(stderr, "Rainfall = %f\n", rain_mm);
}
} else if (bb[2][0] == bb[3][0] && bb[3][0] == bb[4][0] && bb[4][0] == bb[5][0] &&
bb[5][0] == bb[6][0] && (bb[3][4] & 0xf) == 0 && (bb[5][4] & 0xf) == 0) {
//static char * temp_states[4] = {"stable", "increasing", "decreasing", "invalid"};
temp = (int16_t) ((uint16_t) (reverse8(bb[1][1]) >> 4) | (reverse8(bb[1][2]) << 4));
if ((temp & 0x800) != 0) {
temp |= 0xf000;
}
temperature_before_dec = abs(temp / 10);
temperature_after_dec = abs(temp % 10);
humidity = bcd_decode8(reverse8(bb[1][3]));
fprintf(stderr, "\nSensor = Temperature event\n");
fprintf(stderr, "Protocol = AlectoV1 bpr1: %d bpr2: %d\n", bits_per_row[1], bits_per_row[5]);
fprintf(stderr, "Device = %d\n", reverse8(bb[1][0]));
fprintf(stderr, "Channel = %d\n", (bb[1][0] & 0xc) >> 2);
fprintf(stderr, "Button = %d\n", bb[1][1]&0x10 ? 1 : 0);
fprintf(stderr, "Battery = %s\n", bb[1][1]&0x80 ? "Low" : "OK");
fprintf(stderr, "Temp = %s%d.%d\n", temp < 0 ? "-" : "", temperature_before_dec, temperature_after_dec);
fprintf(stderr, "Humidity = %d\n", humidity);
}
fprintf(stderr, "Checksum = %01x (calculated %01x)\n", bb[1][4] >> 4, csum);
fprintf(stderr, "Received Data = %02x %02x %02x %02x %02x\n", bb[1][0], bb[1][1], bb[1][2], bb[1][3], bb[1][4]);
if (wind) fprintf(stderr, "Rcvd Data 2 = %02x %02x %02x %02x %02x\n", bb[5][0], bb[5][1], bb[5][2], bb[5][3], bb[5][4]);
/*
* fprintf(stderr, "L2M: %02x %02x %02x %02x %02x\n",reverse8(bb[1][0]),reverse8(bb[1][1]),reverse8(bb[1][2]),reverse8(bb[1][3]),reverse8(bb[1][4]));
*/
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
return 0;
}
//Timing based on 250000
r_device alectov1 = {
/* .id = */ 11,
/* .name = */ "AlectoV1 Weather Sensor",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 3500 / 4, //875
/* .long_limit = */ 7000 / 4, //1750
/* .reset_limit = */ 15000 / 4, //3750
/* .json_callback = */ &alectov1_callback,
};

147
src/devices/elv.c Normal file
View File

@@ -0,0 +1,147 @@
#include "rtl_433.h"
uint16_t AD_POP(uint8_t bb[BITBUF_COLS], uint8_t bits, uint8_t bit) {
uint16_t val = 0;
uint8_t i, byte_no, bit_no;
for (i=0;i<bits;i++) {
byte_no= (bit+i)/8 ;
bit_no =7-((bit+i)%8);
if (bb[byte_no]&(1<<bit_no)) val = val | (1<<i);
}
return val;
}
static int em1000_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) {
// based on fs20.c
uint8_t dec[10];
uint8_t bytes=0;
uint8_t bit=18; // preamble
uint8_t bb_p[14];
char* types[] = {"S", "?", "GZ"};
uint8_t checksum_calculated = 0;
uint8_t i;
uint8_t stopbit;
uint8_t checksum_received;
// check and combine the 3 repetitions
for (i = 0; i < 14; i++) {
if(bb[0][i]==bb[1][i] || bb[0][i]==bb[2][i]) bb_p[i]=bb[0][i];
else if(bb[1][i]==bb[2][i]) bb_p[i]=bb[1][i];
else return 0;
}
// read 9 bytes with stopbit ...
for (i = 0; i < 9; i++) {
dec[i] = AD_POP (bb_p, 8, bit); bit+=8;
stopbit=AD_POP (bb_p, 1, bit); bit+=1;
if (!stopbit) {
// fprintf(stderr, "!stopbit: %i\n", i);
return 0;
}
checksum_calculated ^= dec[i];
bytes++;
}
// Read checksum
checksum_received = AD_POP (bb_p, 8, bit); bit+=8;
if (checksum_received != checksum_calculated) {
// fprintf(stderr, "checksum_received != checksum_calculated: %d %d\n", checksum_received, checksum_calculated);
return 0;
}
//for (i = 0; i < bytes; i++) fprintf(stderr, "%02X ", dec[i]); fprintf(stderr, "\n");
// based on 15_CUL_EM.pm
fprintf(stderr, "Energy sensor event:\n");
fprintf(stderr, "protocol = ELV EM 1000, %d bits\n",bits_per_row[1]);
fprintf(stderr, "type = EM 1000-%s\n",dec[0]>=1&&dec[0]<=3?types[dec[0]-1]:"?");
fprintf(stderr, "code = %d\n",dec[1]);
fprintf(stderr, "seqno = %d\n",dec[2]);
fprintf(stderr, "total cnt = %d\n",dec[3]|dec[4]<<8);
fprintf(stderr, "current cnt = %d\n",dec[5]|dec[6]<<8);
fprintf(stderr, "peak cnt = %d\n",dec[7]|dec[8]<<8);
return 1;
}
static int ws2000_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) {
// based on http://www.dc3yc.privat.t-online.de/protocol.htm
uint8_t dec[13];
uint8_t nibbles=0;
uint8_t bit=11; // preamble
char* types[]={"!AS3", "AS2000/ASH2000/S2000/S2001A/S2001IA/ASH2200/S300IA", "!S2000R", "!S2000W", "S2001I/S2001ID", "!S2500H", "!Pyrano", "!KS200/KS300"};
uint8_t check_calculated=0, sum_calculated=0;
uint8_t i;
uint8_t stopbit;
uint8_t sum_received;
dec[0] = AD_POP (bb[0], 4, bit); bit+=4;
stopbit= AD_POP (bb[0], 1, bit); bit+=1;
if (!stopbit) {
//fprintf(stderr, "!stopbit\n");
return 0;
}
check_calculated ^= dec[0];
sum_calculated += dec[0];
// read nibbles with stopbit ...
for (i = 1; i <= (dec[0]==4?12:8); i++) {
dec[i] = AD_POP (bb[0], 4, bit); bit+=4;
stopbit= AD_POP (bb[0], 1, bit); bit+=1;
if (!stopbit) {
//fprintf(stderr, "!stopbit %i\n", i);
return 0;
}
check_calculated ^= dec[i];
sum_calculated += dec[i];
nibbles++;
}
if (check_calculated) {
//fprintf(stderr, "check_calculated (%d) != 0\n", check_calculated);
return 0;
}
// Read sum
sum_received = AD_POP (bb[0], 4, bit); bit+=4;
sum_calculated+=5;
sum_calculated&=0xF;
if (sum_received != sum_calculated) {
//fprintf(stderr, "sum_received (%d) != sum_calculated (%d) ", sum_received, sum_calculated);
return 0;
}
//for (i = 0; i < nibbles; i++) fprintf(stderr, "%02X ", dec[i]); fprintf(stderr, "\n");
fprintf(stderr, "Weather station sensor event:\n");
fprintf(stderr, "protocol = ELV WS 2000, %d bits\n",bits_per_row[1]);
fprintf(stderr, "type (!=ToDo) = %s\n", dec[0]<=7?types[dec[0]]:"?");
fprintf(stderr, "code = %d\n", dec[1]&7);
fprintf(stderr, "temp = %s%d.%d\n", dec[1]&8?"-":"", dec[4]*10+dec[3], dec[2]);
fprintf(stderr, "humidity = %d.%d\n", dec[7]*10+dec[6], dec[5]);
if(dec[0]==4) {
fprintf(stderr, "pressure = %d\n", 200+dec[10]*100+dec[9]*10+dec[8]);
}
return 1;
}
r_device elv_em1000 = {
/* .id = */ 7,
/* .name = */ "ELV EM 1000",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 750/4,
/* .long_limit = */ 7250/4,
/* .reset_limit = */ 30000/4,
/* .json_callback = */ &em1000_callback,
};
r_device elv_ws2000 = {
/* .id = */ 8,
/* .name = */ "ELV WS 2000",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ (602+(1155-602)/2)/4,
/* .long_limit = */ ((1755635-1655517)/2)/4, // no repetitions
/* .reset_limit = */ ((1755635-1655517)*2)/4,
/* .json_callback = */ &ws2000_callback,
};

39
src/devices/intertechno.c Normal file
View File

@@ -0,0 +1,39 @@
#include "rtl_433.h"
static int intertechno_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
//if (bb[1][1] == 0 && bb[1][0] != 0 && bb[1][3]==bb[2][3]){
if(bb[0][0]==0 && bb[0][0] == 0 && bb[1][0] == 0x56){
fprintf(stderr, "Switch event:\n");
fprintf(stderr, "protocol = Intertechno\n");
fprintf(stderr, "rid = %x\n",bb[1][0]);
fprintf(stderr, "rid = %x\n",bb[1][1]);
fprintf(stderr, "rid = %x\n",bb[1][2]);
fprintf(stderr, "rid = %x\n",bb[1][3]);
fprintf(stderr, "rid = %x\n",bb[1][4]);
fprintf(stderr, "rid = %x\n",bb[1][5]);
fprintf(stderr, "rid = %x\n",bb[1][6]);
fprintf(stderr, "rid = %x\n",bb[1][7]);
fprintf(stderr, "ADDR Slave = %i\n",bb[1][7] & 0b00001111);
fprintf(stderr, "ADDR Master = %i\n",(bb[1][7] & 0b11110000) >> 4);
fprintf(stderr, "command = %i\n",(bb[1][6] & 0b00000111));
fprintf(stderr, "%02x %02x %02x %02x %02x\n",bb[1][0],bb[1][1],bb[1][2],bb[1][3],bb[1][4]);
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
return 0;
}
r_device intertechno = {
/* .id = */ 11,
/* .name = */ "Intertechno 433",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 100,
/* .long_limit = */ 350,
/* .reset_limit = */ 3000,
/* .json_callback = */ &intertechno_callback,
/* .json_callback = */ //&debug_callback,
};

199
src/devices/lacrosse.c Normal file
View File

@@ -0,0 +1,199 @@
/* LaCrosse TX Temperature and Humidity Sensors
* Tested: TX-7U and TX-6U (Temperature only)
*
* Not Tested but should work: TX-3, TX-4
*
* Protocol Documentation: http://www.f6fbb.org/domo/sensors/tx3_th.php
*
* Message is 44 bits, 11 x 4 bit nybbles:
*
* [00] [cnt = 10] [type] [addr] [addr + parity] [v1] [v2] [v3] [iv1] [iv2] [check]
*
* Notes:
* - Zero Pulses are longer (1,400 uS High, 1,000 uS Low) = 2,400 uS
* - One Pulses are shorter ( 550 uS High, 1,000 uS Low) = 1,600 uS
* - Sensor id changes when the battery is changed
* - Values are BCD with one decimal place: vvv = 12.3
* - Value is repeated integer only iv = 12
* - Temperature is in Celsius with 50.0 added (to handle negative values)
* - There is a 4 bit checksum and a parity bit covering the three digit value
* - Parity check for TX-3 and TX-4 might be different.
* - Msg sent with one repeat after 30 mS
* - Temperature and humidity are sent as separate messages
* - Frequency for each sensor may be could be off by as much as 50-75 khz
*/
#include "rtl_433.h"
// buffer to hold localized timestamp YYYY-MM-DD HH:MM:SS
#define LOCAL_TIME_BUFLEN 32
void local_time_str(time_t time_secs, char *buf) {
time_t etime;
struct tm *tm_info;
if (time_secs == 0) {
time(&etime);
} else {
etime = time_secs;
}
tm_info = localtime(&etime);
strftime(buf, LOCAL_TIME_BUFLEN, "%Y-%m-%d %H:%M:%S", tm_info);
}
// Check for a valid LaCrosse Packet
//
// written for the version of pwm_p_decode() (OOK_PWM_P)
// pulse width detector with two anomalys:
// 1. bits are inverted
// 2. The first bit is discarded as a start bit
//
// If a fixed pulse width decoder is used this
// routine will need to be changed.
static int lacrossetx_detect(uint8_t *pRow, uint8_t *msg_nybbles) {
int i;
uint8_t rbyte_no, rbit_no, mnybble_no, mbit_no;
uint8_t bit, checksum, parity_bit, parity = 0;
// Actual Packet should start with 0x0A and be 6 bytes
// actual message is 44 bit, 11 x 4 bit nybbles.
if ((pRow[0] & 0xFE) == 0x14 && pRow[6] == 0 && pRow[7] == 0) {
for (i = 0; i < 11; i++) {
msg_nybbles[i] = 0;
}
// Move nybbles into a byte array
// shifted by one to compensate for loss of first bit.
for (i = 0; i < 43; i++) {
rbyte_no = i / 8;
rbit_no = 7 - (i % 8);
mnybble_no = (i + 1) / 4;
mbit_no = 3 - ((i + 1) % 4);
bit = (pRow[rbyte_no] & (1 << rbit_no)) ? 1 : 0;
msg_nybbles[mnybble_no] |= (bit << mbit_no);
// Check parity on three bytes of data value
// TX3U might calculate parity on all data including
// sensor id and redundant integer data
if (mnybble_no > 4 && mnybble_no < 8) {
parity += bit;
}
// fprintf(stderr, "recv: [%d/%d] %d -> msg [%d/%d] %02x, Parity: %d %s\n", rbyte_no, rbit_no,
// bit, mnybble_no, mbit_no, msg_nybbles[mnybble_no], parity,
// ( mbit_no == 0 ) ? "\n" : "" );
}
parity_bit = msg_nybbles[4] & 0x01;
parity += parity_bit;
// Validate Checksum (4 bits in last nybble)
checksum = 0;
for (i = 0; i < 10; i++) {
checksum = (checksum + msg_nybbles[i]) & 0x0F;
}
// fprintf(stderr,"Parity: %d, parity bit %d, Good %d\n", parity, parity_bit, parity % 2);
if (checksum == msg_nybbles[10] && (parity % 2 == 0)) {
return 1;
} else {
fprintf(stderr,
"LaCrosse Checksum/Parity error: %d != %d, Parity %d\n",
checksum, msg_nybbles[10], parity);
return 0;
}
}
return 0;
}
// LaCrosse TX-6u, TX-7u, Temperature and Humidity Sensors
// Temperature and Humidity are sent in different messages bursts.
static int lacrossetx_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],
int16_t bits_per_row[BITBUF_ROWS]) {
int i, m, valid = 0;
uint8_t *buf;
uint8_t msg_nybbles[11];
uint8_t sensor_id, msg_type, msg_len, msg_parity, msg_checksum;
int msg_value_int;
float msg_value = 0, temp_c = 0, temp_f = 0;
time_t time_now;
char time_str[25];
static float last_msg_value = 0.0;
static uint8_t last_sensor_id = 0;
static uint8_t last_msg_type = 0;
static time_t last_msg_time = 0;
for (m = 0; m < BITBUF_ROWS; m++) {
valid = 0;
if (lacrossetx_detect(bb[m], msg_nybbles)) {
msg_len = msg_nybbles[1];
msg_type = msg_nybbles[2];
sensor_id = (msg_nybbles[3] << 3) + (msg_nybbles[4] >> 1);
msg_parity = msg_nybbles[4] & 0x01;
msg_value = msg_nybbles[5] * 10 + msg_nybbles[6]
+ msg_nybbles[7] / 10.0;
msg_value_int = msg_nybbles[8] * 10 + msg_nybbles[9];
msg_checksum = msg_nybbles[10];
time(&time_now);
// suppress duplicates
if (sensor_id == last_sensor_id && msg_type == last_msg_type
&& last_msg_value == msg_value
&& time_now - last_msg_time < 50) {
continue;
}
local_time_str(time_now, time_str);
switch (msg_type) {
case 0x00:
temp_c = msg_value - 50.0;
temp_f = temp_c * 1.8 + 32;
printf(
"%s LaCrosse TX Sensor %02x: Temperature %3.1f C / %3.1f F\n",
time_str, sensor_id, temp_c, temp_f);
break;
case 0x0E:
printf("%s LaCrosse TX Sensor %02x: Humidity %3.1f%%\n",
time_str, sensor_id, msg_value);
break;
default:
fprintf(stderr,
"%s LaCrosse Sensor %02x: Unknown Reading % 3.1f (%d)\n",
time_str, sensor_id, msg_value, msg_value_int);
}
time(&last_msg_time);
last_msg_value = msg_value;
last_msg_type = msg_type;
last_sensor_id = sensor_id;
} else {
return 0;
}
}
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
r_device lacrossetx = {
/* .id = */11,
/* .name = */"LaCrosse TX Temperature / Humidity Sensor",
/* .modulation = */OOK_PWM_P,
/* .short_limit = */238,
/* .long_limit = */750,
/* .reset_limit = */8000,
/* .json_callback = */&lacrossetx_callback, };

49
src/devices/mebus.c Normal file
View File

@@ -0,0 +1,49 @@
#include "rtl_433.h"
static int mebus433_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
int temperature_before_dec;
int temperature_after_dec;
int16_t temp;
int8_t hum;
if (bb[0][0] == 0 && bb[1][4] !=0 && (bb[1][0] & 0b01100000) && bb[1][3]==bb[5][3] && bb[1][4] == bb[12][4]){
// Upper 4 bits are stored in nibble 1, lower 8 bits are stored in nibble 2
// upper 4 bits of nibble 1 are reserved for other usages.
temp = (int16_t)((uint16_t)(bb[1][1] << 12 ) | bb[1][2]<< 4);
temp = temp >> 4;
// lower 4 bits of nibble 3 and upper 4 bits of nibble 4 contains
// humidity as decimal value
hum = (bb[1][3] << 4 | bb[1][4] >> 4);
temperature_before_dec = abs(temp / 10);
temperature_after_dec = abs(temp % 10);
fprintf(stderr, "Sensor event:\n");
fprintf(stderr, "protocol = Mebus/433\n");
fprintf(stderr, "address = %i\n", bb[1][0] & 0b00011111);
fprintf(stderr, "channel = %i\n",((bb[1][1] & 0b00110000) >> 4)+1);
fprintf(stderr, "battery = %s\n", bb[1][1] & 0b10000000?"Ok":"Low");
fprintf(stderr, "unkown1 = %i\n",(bb[1][1] & 0b01000000) >> 6); // always 0?
fprintf(stderr, "unkown2 = %i\n",(bb[1][3] & 0b11110000) >> 4); // always 1111?
fprintf(stderr, "temperature = %s%d.%d°C\n",temp<0?"-":"",temperature_before_dec, temperature_after_dec);
fprintf(stderr, "humidity = %i%%\n", hum);
fprintf(stderr, "%02x %02x %02x %02x %02x\n",bb[1][0],bb[1][1],bb[1][2],bb[1][3],bb[1][4]);
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
return 0;
}
r_device mebus433 = {
/* .id = */ 10,
/* .name = */ "Mebus 433",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 300,
/* .long_limit = */ 600,
/* .reset_limit = */ 1500,
/* .json_callback = */ &mebus433_callback,
/* .json_callback = */ //&debug_callback,
};

127
src/devices/newkaku.c Normal file
View File

@@ -0,0 +1,127 @@
#include "rtl_433.h"
static int newkaku_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
/* Two bits map to 2 states, 0 1 -> 0 and 1 1 -> 1 */
/* Status bit can be 1 1 -> 1 which indicates DIM value. 4 extra bits are present with value */
/*start pulse: 1T high, 10.44T low */
/*- 26 bit: Address */
/*- 1 bit: group bit*/
/*- 1 bit: Status bit on/off/[dim]*/
/*- 4 bit: unit*/
/*- [4 bit: dim level. Present if [dim] is used, but might be present anyway...]*/
/*- stop pulse: 1T high, 40T low */
int i;
uint8_t tmp = 0;
uint8_t unit = 0;
uint8_t packet = 0;
uint8_t bitcount = 0;
uint32_t kakuid = 0;
if (bb[0][0] == 0xac) {//allways starts with ac
// first bit is from startbit sequence, not part of payload!
// check protocol if value is 10 or 01, else stop processing as it is no vallid KAKU packet!
//get id=24bits, remember 1st 1 bit = startbit, no payload!
for (packet = 0; packet < 6; packet++) {//get first part kakuid
tmp = bb[0][packet] << 1;
if ((bb[0][packet + 1]&(1 << 7)) != 0) {// if set add bit to current
tmp++;
}
for (bitcount = 0; bitcount < 8; bitcount += 2) {//process bitstream, check protocol!
if (((tmp << bitcount & (0x80)) == 0x80)&((tmp << bitcount & (0x40)) == 0)) {
//add 1
kakuid = kakuid << 1;
kakuid++;
} else
if (((tmp << bitcount & (0x80)) == 0)&((tmp << bitcount & (0x40)) == 0x40)) {
kakuid = kakuid << 1;
//add 0
} else {
return 0; //00 and 11 indicates packet error. Do exit, no valid packet
}
}
}
tmp = bb[0][6] << 1; //Get last part ID
for (bitcount = 0; bitcount < 4; bitcount += 2) {
if (((tmp << bitcount & (0x80)) == 0x80)&((tmp << bitcount & (0x40)) == 0)) {
//add 1
kakuid = kakuid << 1;
kakuid++;
} else
if (((tmp << bitcount & (0x80)) == 0)&((tmp << bitcount & (0x40)) == 0x40)) {
//= add bit on kakuid
kakuid = kakuid << 1;
//add 0
} else {
//fprintf(stderr, " Packet error, no newkaku!!\n", tmp << bitcount);
return 0; //00 and 11 indicates packet error. no valid packet! do exit
}
}
//Get unit ID
tmp = bb[0][7] << 1;
if ((bb[0][8]&(1 << 7)) != 0) {// if set add bit to current
tmp++;
}
for (bitcount = 0; bitcount < 8; bitcount += 2) {//process bitstream, check protocol!
if (((tmp << bitcount & (0x80)) == 0x80)&((tmp << bitcount & (0x40)) == 0)) {
//add 1
unit = unit << 1;
unit++;
} else
if (((tmp << bitcount & (0x80)) == 0)&((tmp << bitcount & (0x40)) == 0x40)) {
unit = unit << 1;
//add 0
} else {
return 0; //00 and 11 indicates packet error. Do exit, no valid packet
}
}
fprintf(stderr, "NewKaku event:\n");
fprintf(stderr, "Model = NewKaKu on/off/dimmer switch\n");
fprintf(stderr, "KakuId = %d (H%.2X)\n", kakuid, kakuid);
fprintf(stderr, "Unit = %d (H%.2X)\n", unit, unit);
fprintf(stderr, "Group Call = %s\n", (((bb[0][6] & (0x04)) == 0x04)&((bb[0][6] & (0x02)) == 0)) ? "Yes" : "No");
fprintf(stderr, "Command = %s\n", (((bb[0][6] & (0x01)) == 0x01)&((bb[0][7] & (0x80)) == 0)) ? "On" : "Off");
if (((bb[0][6] & (0x01)) == 0x01)&((bb[0][7] & (0x80)) == 0x80)) {//11 indicates DIM command, 4 extra bits indicate DIM value
fprintf(stderr, "Dim = Yes\n");
tmp = bb[0][8] << 1; // get packet, loose first bit
uint8_t dv = 0;
if ((bb[0][9]&(1 << 7)) != 0) {// if bit is set Add to current packet
tmp++;
for (bitcount = 0; bitcount < 8; bitcount += 2) {//process last bit outside
if (((tmp << bitcount & (0x80)) == 0x80)&((tmp << bitcount & (0x40)) == 0)) {
//add 1
dv = dv << 1;
dv++;
} else
if (((tmp << bitcount & (0x80)) == 0)&((tmp << bitcount & (0x40)) == 0x40)) {
dv = dv << 1;
//add 0
} else {
return 0; //00 and 11 indicates packet error. Do exit, no valid packet
}
}
}
fprintf(stderr, "Dim Value = %d\n", dv);
} else {
fprintf(stderr, "Dim = No\n");
fprintf(stderr, "Dim Value = 0\n");
}
fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
bb[0][0], bb[0][1], bb[0][2], bb[0][3], bb[0][4], bb[0][5], bb[0][6], bb[0][7], bb[0][8]);
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
return 0;
}
r_device newkaku = {
/* .id = */ 11,
/* .name = */ "KlikAanKlikUit Wireless Switch",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 200,
/* .long_limit = */ 800,
/* .reset_limit = */ 4000,
/* .json_callback = */ &newkaku_callback,
};

View File

@@ -0,0 +1,280 @@
#include "rtl_433.h"
float get_os_temperature(unsigned char *message, unsigned int sensor_id) {
// sensor ID included to support sensors with temp in different position
float temp_c = 0;
temp_c = (((message[5]>>4)*100)+((message[4]&0x0f)*10) + ((message[4]>>4)&0x0f)) /10.0F;
if (message[5] & 0x0f)
temp_c = -temp_c;
return temp_c;
}
unsigned int get_os_humidity(unsigned char *message, unsigned int sensor_id) {
// sensor ID included to support sensors with temp in different position
int humidity = 0;
humidity = ((message[6]&0x0f)*10)+(message[6]>>4);
return humidity;
}
static int validate_os_checksum(unsigned char *msg, int checksum_nibble_idx) {
// Oregon Scientific v2.1 and v3 checksum is a 1 byte 'sum of nibbles' checksum.
// with the 2 nibbles of the checksum byte swapped.
int i;
unsigned int checksum, sum_of_nibbles=0;
for (i=0; i<(checksum_nibble_idx-1);i+=2) {
unsigned char val=msg[i>>1];
sum_of_nibbles += ((val>>4) + (val &0x0f));
}
if (checksum_nibble_idx & 1) {
sum_of_nibbles += (msg[checksum_nibble_idx>>1]>>4);
checksum = (msg[checksum_nibble_idx>>1] & 0x0f) | (msg[(checksum_nibble_idx+1)>>1]&0xf0);
} else
checksum = (msg[checksum_nibble_idx>>1]>>4) | ((msg[checksum_nibble_idx>>1]&0x0f)<<4);
sum_of_nibbles &= 0xff;
if (sum_of_nibbles == checksum)
return 0;
else {
fprintf(stderr, "Checksum error in Oregon Scientific message. Expected: %02x Calculated: %02x\n", checksum, sum_of_nibbles);
fprintf(stderr, "Message: "); int i; for (i=0 ;i<((checksum_nibble_idx+4)>>1) ; i++) fprintf(stderr, "%02x ", msg[i]); fprintf(stderr, "\n\n");
return 1;
}
}
static int validate_os_v2_message(unsigned char * msg, int bits_expected, int valid_v2_bits_received,
int nibbles_in_checksum) {
// Oregon scientific v2.1 protocol sends each bit using the complement of the bit, then the bit for better error checking. Compare number of valid bits processed vs number expected
if (bits_expected == valid_v2_bits_received) {
return (validate_os_checksum(msg, nibbles_in_checksum));
} else {
fprintf(stderr, "Bit validation error on Oregon Scientific message. Expected %d bits, received error after bit %d \n", bits_expected, valid_v2_bits_received);
fprintf(stderr, "Message: "); int i; for (i=0 ;i<(bits_expected+7)/8 ; i++) fprintf(stderr, "%02x ", msg[i]); fprintf(stderr, "\n\n");
}
return 1;
}
static int oregon_scientific_v2_1_parser(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
// Check 2nd and 3rd bytes of stream for possible Oregon Scientific v2.1 sensor data (skip first byte to get past sync/startup bit errors)
if ( ((bb[0][1] == 0x55) && (bb[0][2] == 0x55)) ||
((bb[0][1] == 0xAA) && (bb[0][2] == 0xAA))) {
int i,j;
unsigned char msg[BITBUF_COLS] = {0};
// Possible v2.1 Protocol message
int num_valid_v2_bits = 0;
unsigned int sync_test_val = (bb[0][3]<<24) | (bb[0][4]<<16) | (bb[0][5]<<8) | (bb[0][6]);
int dest_bit = 0;
int pattern_index;
// Could be extra/dropped bits in stream. Look for sync byte at expected position +/- some bits in either direction
for(pattern_index=0; pattern_index<8; pattern_index++) {
unsigned int mask = (unsigned int) (0xffff0000>>pattern_index);
unsigned int pattern = (unsigned int)(0x55990000>>pattern_index);
unsigned int pattern2 = (unsigned int)(0xaa990000>>pattern_index);
//fprintf(stderr, "OS v2.1 sync byte search - test_val=%08x pattern=%08x mask=%08x\n", sync_test_val, pattern, mask);
if (((sync_test_val & mask) == pattern) ||
((sync_test_val & mask) == pattern2)) {
// Found sync byte - start working on decoding the stream data.
// pattern_index indicates where sync nibble starts, so now we can find the start of the payload
int start_byte = 5 + (pattern_index>>3);
int start_bit = pattern_index & 0x07;
//fprintf(stderr, "OS v2.1 Sync test val %08x found, starting decode at byte index %d bit %d\n", sync_test_val, start_byte, start_bit);
int bits_processed = 0;
unsigned char last_bit_val = 0;
j=start_bit;
for (i=start_byte;i<BITBUF_COLS;i++) {
while (j<8) {
if (bits_processed & 0x01) {
unsigned char bit_val = ((bb[0][i] & (0x80 >> j)) >> (7-j));
// check if last bit received was the complement of the current bit
if ((num_valid_v2_bits == 0) && (last_bit_val == bit_val))
num_valid_v2_bits = bits_processed; // record position of first bit in stream that doesn't verify correctly
last_bit_val = bit_val;
// copy every other bit from source stream to dest packet
msg[dest_bit>>3] |= (((bb[0][i] & (0x80 >> j)) >> (7-j)) << (7-(dest_bit & 0x07)));
//fprintf(stderr,"i=%d j=%d dest_bit=%02x bb=%02x msg=%02x\n",i, j, dest_bit, bb[0][i], msg[dest_bit>>3]);
if ((dest_bit & 0x07) == 0x07) {
// after assembling each dest byte, flip bits in each nibble to convert from lsb to msb bit ordering
int k = (dest_bit>>3);
unsigned char indata = msg[k];
// flip the 4 bits in the upper and lower nibbles
msg[k] = ((indata & 0x11) << 3) | ((indata & 0x22) << 1) |
((indata & 0x44) >> 1) | ((indata & 0x88) >> 3);
}
dest_bit++;
}
else
last_bit_val = ((bb[0][i] & (0x80 >> j)) >> (7-j)); // used for v2.1 bit error detection
bits_processed++;
j++;
}
j=0;
}
break;
} //if (sync_test_val...
} // for (pattern...
int sensor_id = (msg[0] << 8) | msg[1];
if ((sensor_id == 0x1d20) || (sensor_id == 0x1d30)) {
if (validate_os_v2_message(msg, 153, num_valid_v2_bits, 15) == 0) {
int channel = ((msg[2] >> 4)&0x0f);
if (channel == 4)
channel = 3; // sensor 3 channel number is 0x04
float temp_c = get_os_temperature(msg, sensor_id);
if (sensor_id == 0x1d20) fprintf(stderr, "Weather Sensor THGR122N Channel %d ", channel);
else fprintf(stderr, "Weather Sensor THGR968 Outdoor ");
fprintf(stderr, "Temp: %3.1f°C %3.1f°F Humidity: %d%%\n", temp_c, ((temp_c*9)/5)+32,get_os_humidity(msg, sensor_id));
}
return 1;
} else if (sensor_id == 0x5d60) {
if (validate_os_v2_message(msg, 185, num_valid_v2_bits, 19) == 0) {
unsigned int comfort = msg[7] >>4;
char *comfort_str="Normal";
if (comfort == 4) comfort_str = "Comfortable";
else if (comfort == 8) comfort_str = "Dry";
else if (comfort == 0xc) comfort_str = "Humid";
unsigned int forecast = msg[9]>>4;
char *forecast_str="Cloudy";
if (forecast == 3) forecast_str = "Rainy";
else if (forecast == 6) forecast_str = "Partly Cloudy";
else if (forecast == 0xc) forecast_str = "Sunny";
float temp_c = get_os_temperature(msg, 0x5d60);
fprintf(stderr,"Weather Sensor BHTR968 Indoor Temp: %3.1f°C %3.1f°F Humidity: %d%%", temp_c, ((temp_c*9)/5)+32, get_os_humidity(msg, 0x5d60));
fprintf(stderr, " (%s) Pressure: %dmbar (%s)\n", comfort_str, ((msg[7] & 0x0f) | (msg[8] & 0xf0))+856, forecast_str);
}
return 1;
} else if (sensor_id == 0x2d10) {
if (validate_os_v2_message(msg, 161, num_valid_v2_bits, 16) == 0) {
float rain_rate = (((msg[4] &0x0f)*100)+((msg[4]>>4)*10) + ((msg[5]>>4)&0x0f)) /10.0F;
float total_rain = (((msg[7]&0xf)*10000)+((msg[7]>>4)*1000) + ((msg[6]&0xf)*100)+((msg[6]>>4)*10) + (msg[5]&0xf))/10.0F;
fprintf(stderr, "Weather Sensor RGR968 Rain Gauge Rain Rate: %2.0fmm/hr Total Rain %3.0fmm\n", rain_rate, total_rain);
}
return 1;
} else if (sensor_id == 0xec40 && num_valid_v2_bits==153) {
if ( validate_os_v2_message(msg, 153, num_valid_v2_bits, 12) == 0) {
int channel = ((msg[2] >> 4)&0x0f);
if (channel == 4)
channel = 3; // sensor 3 channel number is 0x04
float temp_c = get_os_temperature(msg, sensor_id);
if (sensor_id == 0xec40) fprintf(stderr, "Thermo Sensor THR228N Channel %d ", channel);
fprintf(stderr, "Temp: %3.1f°C %3.1f°F\n", temp_c, ((temp_c*9)/5)+32);
}
return 1;
} else if (sensor_id == 0xec40 && num_valid_v2_bits==129) {
if ( validate_os_v2_message(msg, 129, num_valid_v2_bits, 12) == 0) {
int channel = ((msg[2] >> 4)&0x0f);
if (channel == 4)
channel = 3; // sensor 3 channel number is 0x04
int battery_low = (msg[3] >> 2 & 0x01);
unsigned char rolling_code = ((msg[2] << 4)&0xF0) | ((msg[3] >> 4)&0x0F);
float temp_c = get_os_temperature(msg, sensor_id);
if (sensor_id == 0xec40) fprintf(stderr, "Thermo Sensor THN132N, Channel %d, Battery: %s, Rolling-code 0x%0X, ", channel, battery_low?"Low":"Ok", rolling_code);
fprintf(stderr, "Temp: %3.1f°C %3.1f°F\n", temp_c, ((temp_c*9)/5)+32);
}
return 1;
} else if (num_valid_v2_bits > 16) {
fprintf(stderr, "%d bit message received from unrecognized Oregon Scientific v2.1 sensor with device ID %x.\n", num_valid_v2_bits, sensor_id);
fprintf(stderr, "Message: "); for (i=0 ; i<20 ; i++) fprintf(stderr, "%02x ", msg[i]); fprintf(stderr,"\n\n");
} else {
//fprintf(stderr, "\nPossible Oregon Scientific v2.1 message, but sync nibble wasn't found\n"); fprintf(stderr, "Raw Data: "); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n");
}
} else {
//if (bb[0][3] != 0) int i; fprintf(stderr, "\nBadly formatted OS v2.1 message encountered."); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n");}
}
return 0;
}
static int oregon_scientific_v3_parser(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
// Check stream for possible Oregon Scientific v3 protocol data (skip part of first and last bytes to get past sync/startup bit errors)
if ((((bb[0][0]&0xf) == 0x0f) && (bb[0][1] == 0xff) && ((bb[0][2]&0xc0) == 0xc0)) ||
(((bb[0][0]&0xf) == 0x00) && (bb[0][1] == 0x00) && ((bb[0][2]&0xc0) == 0x00))) {
int i,j;
unsigned char msg[BITBUF_COLS] = {0};
unsigned int sync_test_val = (bb[0][2]<<24) | (bb[0][3]<<16) | (bb[0][4]<<8);
int dest_bit = 0;
int pattern_index;
// Could be extra/dropped bits in stream. Look for sync byte at expected position +/- some bits in either direction
for(pattern_index=0; pattern_index<16; pattern_index++) {
unsigned int mask = (unsigned int)(0xfff00000>>pattern_index);
unsigned int pattern = (unsigned int)(0xffa00000>>pattern_index);
unsigned int pattern2 = (unsigned int)(0xff500000>>pattern_index);
unsigned int pattern3 = (unsigned int)(0x00500000>>pattern_index);
//fprintf(stderr, "OS v3 Sync nibble search - test_val=%08x pattern=%08x mask=%08x\n", sync_test_val, pattern, mask);
if (((sync_test_val & mask) == pattern) ||
((sync_test_val & mask) == pattern2) ||
((sync_test_val & mask) == pattern3)) {
// Found sync byte - start working on decoding the stream data.
// pattern_index indicates where sync nibble starts, so now we can find the start of the payload
int start_byte = 3 + (pattern_index>>3);
int start_bit = (pattern_index+4) & 0x07;
//fprintf(stderr, "Oregon Scientific v3 Sync test val %08x ok, starting decode at byte index %d bit %d\n", sync_test_val, start_byte, start_bit);
j = start_bit;
for (i=start_byte;i<BITBUF_COLS;i++) {
while (j<8) {
unsigned char bit_val = ((bb[0][i] & (0x80 >> j)) >> (7-j));
// copy every bit from source stream to dest packet
msg[dest_bit>>3] |= (((bb[0][i] & (0x80 >> j)) >> (7-j)) << (7-(dest_bit & 0x07)));
//fprintf(stderr,"i=%d j=%d dest_bit=%02x bb=%02x msg=%02x\n",i, j, dest_bit, bb[0][i], msg[dest_bit>>3]);
if ((dest_bit & 0x07) == 0x07) {
// after assembling each dest byte, flip bits in each nibble to convert from lsb to msb bit ordering
int k = (dest_bit>>3);
unsigned char indata = msg[k];
// flip the 4 bits in the upper and lower nibbles
msg[k] = ((indata & 0x11) << 3) | ((indata & 0x22) << 1) |
((indata & 0x44) >> 1) | ((indata & 0x88) >> 3);
}
dest_bit++;
j++;
}
j=0;
}
break;
}
}
if ((msg[0] == 0xf8) && (msg[1] == 0x24)) {
if (validate_os_checksum(msg, 15) == 0) {
int channel = ((msg[2] >> 4)&0x0f);
float temp_c = get_os_temperature(msg, 0xf824);
int humidity = get_os_humidity(msg, 0xf824);
fprintf(stderr,"Weather Sensor THGR810 Channel %d Temp: %3.1f°C %3.1f°F Humidity: %d%%\n", channel, temp_c, ((temp_c*9)/5)+32, humidity);
}
return 1;
} else if ((msg[0] != 0) && (msg[1]!= 0)) { // sync nibble was found and some data is present...
fprintf(stderr, "Message received from unrecognized Oregon Scientific v3 sensor.\n");
fprintf(stderr, "Message: "); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", msg[i]); fprintf(stderr, "\n");
fprintf(stderr, " Raw: "); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n");
} else if (bb[0][3] != 0) {
//fprintf(stderr, "\nPossible Oregon Scientific v3 message, but sync nibble wasn't found\n"); fprintf(stderr, "Raw Data: "); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n");
}
}
else { // Based on first couple of bytes, either corrupt message or something other than an Oregon Scientific v3 message
//if (bb[0][3] != 0) { fprintf(stderr, "\nUnrecognized Msg in v3: "); int i; for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n"); }
}
return 0;
}
static int oregon_scientific_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
int ret = oregon_scientific_v2_1_parser(bb, bits_per_row);
if (ret == 0)
ret = oregon_scientific_v3_parser(bb, bits_per_row);
return ret;
}
r_device oregon_scientific = {
/* .id = */ 11,
/* .name = */ "Oregon Scientific Weather Sensor",
/* .modulation = */ OOK_MANCHESTER,
/* .short_limit = */ 125,
/* .long_limit = */ 0, // not used
/* .reset_limit = */ 600,
/* .json_callback = */ &oregon_scientific_callback,
};

67
src/devices/prologue.c Normal file
View File

@@ -0,0 +1,67 @@
/* Prologue sensor protocol
*
* the sensor sends 36 bits 7 times, before the first packet there is a pulse sent
* the packets are pwm modulated
*
* the data is grouped in 9 nibles
* [id0] [rid0] [rid1] [data0] [temp0] [temp1] [temp2] [humi0] [humi1]
*
* id0 is always 1001,9
* rid is a random id that is generated when the sensor starts, could include battery status
* the same batteries often generate the same id
* data(3) is 0 the battery status, 1 ok, 0 low, first reading always say low
* data(2) is 1 when the sensor sends a reading when pressing the button on the sensor
* data(1,0)+1 forms the channel number that can be set by the sensor (1-3)
* temp is 12 bit signed scaled by 10
* humi0 is always 1100,c if no humidity sensor is available
* humi1 is always 1100,c if no humidity sensor is available
*
* The sensor can be bought at Clas Ohlson
*/
#include "rtl_433.h"
static int prologue_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) {
int rid;
int16_t temp2;
/* FIXME validate the received message better */
if (((bb[1][0]&0xF0) == 0x90 && (bb[2][0]&0xF0) == 0x90 && (bb[3][0]&0xF0) == 0x90 && (bb[4][0]&0xF0) == 0x90 &&
(bb[5][0]&0xF0) == 0x90 && (bb[6][0]&0xF0) == 0x90) ||
((bb[1][0]&0xF0) == 0x50 && (bb[2][0]&0xF0) == 0x50 && (bb[3][0]&0xF0) == 0x50 && (bb[4][0]&0xF0) == 0x50) &&
(bb[1][3] == bb[2][3]) && (bb[1][4] == bb[2][4])) {
/* Prologue sensor */
temp2 = (int16_t)((uint16_t)(bb[1][2] << 8) | (bb[1][3]&0xF0));
temp2 = temp2 >> 4;
fprintf(stderr, "Sensor temperature event:\n");
fprintf(stderr, "protocol = Prologue, %d bits\n",bits_per_row[1]);
fprintf(stderr, "button = %d\n",bb[1][1]&0x04?1:0);
fprintf(stderr, "battery = %s\n",bb[1][1]&0x08?"Ok":"Low");
fprintf(stderr, "temp = %s%d.%d\n",temp2<0?"-":"",abs((int16_t)temp2/10),abs((int16_t)temp2%10));
fprintf(stderr, "humidity = %d\n", ((bb[1][3]&0x0F)<<4)|(bb[1][4]>>4));
fprintf(stderr, "channel = %d\n",(bb[1][1]&0x03)+1);
fprintf(stderr, "id = %d\n",(bb[1][0]&0xF0)>>4);
rid = ((bb[1][0]&0x0F)<<4)|(bb[1][1]&0xF0)>>4;
fprintf(stderr, "rid = %d\n", rid);
fprintf(stderr, "hrid = %02x\n", rid);
fprintf(stderr, "%02x %02x %02x %02x %02x\n",bb[1][0],bb[1][1],bb[1][2],bb[1][3],bb[1][4]);
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
return 0;
}
r_device prologue = {
/* .id = */ 2,
/* .name = */ "Prologue Temperature Sensor",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 3500/4,
/* .long_limit = */ 7000/4,
/* .reset_limit = */ 15000/4,
/* .json_callback = */ &prologue_callback,
};

59
src/devices/rubicson.c Normal file
View File

@@ -0,0 +1,59 @@
#include "rtl_433.h"
/* Currently this can decode the temperature and id from Rubicson sensors
*
* the sensor sends 36 bits 12 times pwm modulated
* the data is grouped into 9 nibles
* [id0] [id1], [unk0] [temp0], [temp1] [temp2], [unk1] [unk2], [unk3]
*
* The id changes when the battery is changed in the sensor.
* unk0 is always 1 0 0 0, most likely 2 channel bits as the sensor can recevice 3 channels
* unk1-3 changes and the meaning is unknown
* temp is 12 bit signed scaled by 10
*
* The sensor can be bought at Kjell&Co
*/
static int rubicson_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) {
int temperature_before_dec;
int temperature_after_dec;
int16_t temp;
/* FIXME validate the received message better, figure out crc */
if (bb[1][0] == bb[2][0] && bb[2][0] == bb[3][0] && bb[3][0] == bb[4][0] &&
bb[4][0] == bb[5][0] && bb[5][0] == bb[6][0] && bb[6][0] == bb[7][0] && bb[7][0] == bb[8][0] &&
bb[8][0] == bb[9][0] && (bb[5][0] != 0 && bb[5][1] != 0 && bb[5][2] != 0)) {
/* Nible 3,4,5 contains 12 bits of temperature
* The temerature is signed and scaled by 10 */
temp = (int16_t)((uint16_t)(bb[0][1] << 12) | (bb[0][2] << 4));
temp = temp >> 4;
temperature_before_dec = abs(temp / 10);
temperature_after_dec = abs(temp % 10);
fprintf(stderr, "Sensor temperature event:\n");
fprintf(stderr, "protocol = Rubicson/Auriol, %d bits\n",bits_per_row[1]);
fprintf(stderr, "rid = %x\n",bb[0][0]);
fprintf(stderr, "temp = %s%d.%d\n",temp<0?"-":"",temperature_before_dec, temperature_after_dec);
fprintf(stderr, "%02x %02x %02x %02x %02x\n",bb[1][0],bb[0][1],bb[0][2],bb[0][3],bb[0][4]);
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
return 0;
}
// timings based on samp_rate=1024000
r_device rubicson = {
/* .id = */ 1,
/* .name = */ "Rubicson Temperature Sensor",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 1744/4,
/* .long_limit = */ 3500/4,
/* .reset_limit = */ 5000/4,
/* .json_callback = */ &rubicson_callback,
};

34
src/devices/silvercrest.c Normal file
View File

@@ -0,0 +1,34 @@
#include "rtl_433.h"
static int silvercrest_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) {
/* FIXME validate the received message better */
if (bb[1][0] == 0xF8 &&
bb[2][0] == 0xF8 &&
bb[3][0] == 0xF8 &&
bb[4][0] == 0xF8 &&
bb[1][1] == 0x4d &&
bb[2][1] == 0x4d &&
bb[3][1] == 0x4d &&
bb[4][1] == 0x4d) {
/* Pretty sure this is a Silvercrest remote */
fprintf(stderr, "Remote button event:\n");
fprintf(stderr, "model = Silvercrest, %d bits\n",bits_per_row[1]);
fprintf(stderr, "%02x %02x %02x %02x %02x\n",bb[1][0],bb[0][1],bb[0][2],bb[0][3],bb[0][4]);
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
return 0;
}
r_device silvercrest = {
/* .id = */ 3,
/* .name = */ "Silvercrest Remote Control",
/* .modulation = */ OOK_PWM_P,
/* .short_limit = */ 600/4,
/* .long_limit = */ 5000/4,
/* .reset_limit = */ 15000/4,
/* .json_callback = */ &silvercrest_callback,
};

46
src/devices/steffen.c Normal file
View File

@@ -0,0 +1,46 @@
#include "rtl_433.h"
static int steffen_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) {
if (bb[0][0]==0x00 && ((bb[1][0]&0x07)==0x07) && bb[1][0]==bb[2][0] && bb[2][0]==bb[3][0]) {
fprintf(stderr, "Remote button event:\n");
fprintf(stderr, "model = Steffan Switch Transmitter, %d bits\n",bits_per_row[1]);
fprintf(stderr, "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);
if ((bb[1][2]&0x0f)==0x0e)
fprintf(stderr, "button = A\n");
else if ((bb[1][2]&0x0f)==0x0d)
fprintf(stderr, "button = B\n");
else if ((bb[1][2]&0x0f)==0x0b)
fprintf(stderr, "button = C\n");
else if ((bb[1][2]&0x0f)==0x07)
fprintf(stderr, "button = D\n");
else if ((bb[1][2]&0x0f)==0x0f)
fprintf(stderr, "button = ALL\n");
else
fprintf(stderr, "button = unknown\n");
if ((bb[1][2]&0xf0)==0xf0) {
fprintf(stderr, "state = OFF\n");
} else {
fprintf(stderr, "state = ON\n");
}
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
return 0;
}
r_device steffen = {
/* .id = */ 9,
/* .name = */ "Steffen Switch Transmitter",
/* .modulation = */ OOK_PWM_D,
/* .short_limit = */ 140,
/* .long_limit = */ 270,
/* .reset_limit = */ 1500,
/* .json_callback = */ &steffen_callback,
};

40
src/devices/waveman.c Normal file
View File

@@ -0,0 +1,40 @@
#include "rtl_433.h"
static int waveman_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) {
/* Two bits map to 2 states, 0 1 -> 0 and 1 1 -> 1 */
int i;
uint8_t nb[3] = {0};
if (((bb[0][0]&0x55)==0x55) && ((bb[0][1]&0x55)==0x55) && ((bb[0][2]&0x55)==0x55) && ((bb[0][3]&0x55)==0x00)) {
for (i=0 ; i<3 ; i++) {
nb[i] |= ((bb[0][i]&0xC0)==0xC0) ? 0x00 : 0x01;
nb[i] |= ((bb[0][i]&0x30)==0x30) ? 0x00 : 0x02;
nb[i] |= ((bb[0][i]&0x0C)==0x0C) ? 0x00 : 0x04;
nb[i] |= ((bb[0][i]&0x03)==0x03) ? 0x00 : 0x08;
}
fprintf(stderr, "Remote button event:\n");
fprintf(stderr, "model = Waveman Switch Transmitter, %d bits\n",bits_per_row[1]);
fprintf(stderr, "id = %c\n", 'A'+nb[0]);
fprintf(stderr, "channel = %d\n", (nb[1]>>2)+1);
fprintf(stderr, "button = %d\n", (nb[1]&3)+1);
fprintf(stderr, "state = %s\n", (nb[2]==0xe) ? "on" : "off");
fprintf(stderr, "%02x %02x %02x\n",nb[0],nb[1],nb[2]);
if (debug_output)
debug_callback(bb, bits_per_row);
return 1;
}
return 0;
}
r_device waveman = {
/* .id = */ 6,
/* .name = */ "Waveman Switch Transmitter",
/* .modulation = */ OOK_PWM_P,
/* .short_limit = */ 1000/4,
/* .long_limit = */ 8000/4,
/* .reset_limit = */ 30000/4,
/* .json_callback = */ &waveman_callback,
};

1485
src/rtl_433.c Executable file → Normal file
View File

File diff suppressed because it is too large Load Diff