mirror of
https://github.com/merbanan/rtl_433.git
synced 2026-04-29 13:52:45 -04:00
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:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -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
17
README
@@ -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
54
include/rtl_433.h
Normal 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
21
include/rtl_433_devices.h
Normal 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_ */
|
||||
@@ -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
205
src/devices/acurite.c
Normal 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
163
src/devices/alecto.c
Normal 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
147
src/devices/elv.c
Normal 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
39
src/devices/intertechno.c
Normal 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
199
src/devices/lacrosse.c
Normal 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
49
src/devices/mebus.c
Normal 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
127
src/devices/newkaku.c
Normal 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,
|
||||
};
|
||||
280
src/devices/oregon_scientific.c
Normal file
280
src/devices/oregon_scientific.c
Normal 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
67
src/devices/prologue.c
Normal 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
59
src/devices/rubicson.c
Normal 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
34
src/devices/silvercrest.c
Normal 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
46
src/devices/steffen.c
Normal 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
40
src/devices/waveman.c
Normal 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
1485
src/rtl_433.c
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user