Add support for Philips outdoor temperature sensor

This sensor is used with various Philips clock radios.
This commit is contained in:
Chris Coffey
2017-11-01 09:31:52 +00:00
committed by Benjamin Larsson
parent 0df7ac14ab
commit 2e720e68c4
6 changed files with 238 additions and 3 deletions

View File

@@ -178,6 +178,7 @@ Supported device protocols:
[91] Infactory
[92] Ft004b
[93] Ford car remote
[94] Philips outdoor temperature sensor
* Disabled by default, use -R n or -G

View File

@@ -42,7 +42,7 @@
#define MINIMAL_BUF_LENGTH 512
#define MAXIMAL_BUF_LENGTH (256 * 16384)
#define MAX_PROTOCOLS 93
#define MAX_PROTOCOLS 94
#define SIGNAL_GRABBER_BUFFER (12 * DEFAULT_BUF_LENGTH)
/* Supported modulation types */

View File

@@ -96,7 +96,8 @@
DECL(tpms_renault) \
DECL(infactory) \
DECL(ft004b) \
DECL(fordremote)
DECL(fordremote) \
DECL(philips)
typedef struct {

View File

@@ -105,6 +105,7 @@ add_executable(rtl_433
devices/tpms_renault.c
devices/infactory.c
devices/fordremote.c
devices/philips.c
)

View File

@@ -90,6 +90,7 @@ rtl_433_SOURCES = baseband.c \
devices/tpms_ford.c \
devices/tpms_renault.c \
devices/infactory.c \
devices/fordremote.c
devices/fordremote.c \
devices/philips.c
rtl_433_LDADD = $(LIBRTLSDR) $(LIBM)

231
src/devices/philips.c Normal file
View File

@@ -0,0 +1,231 @@
/*
* Philips outdoor temperature sensor -- used with various Philips clock
* radios (tested on AJ3650)
*
* Not tested, but these should also work: AJ7010, AJ260 ... maybe others?
*
* A complete message is 112 bits:
* 4-bit initial preamble, always 0
* 4-bit packet separator, always 0, followed by 32-bit data packet.
* Packets are repeated 3 times for 108 bits total.
*
* 32-bit data packet format:
*
* 0001cccc tttttttt tt000000 0b0?ssss
*
* c - channel: 0=channel 2, 2=channel 1, 4=channel 3 (4 bits)
* t - temperature in Celsius: subtract 500 and divide by 10 (10 bits)
* b - battery status: 0 = OK, 1 = LOW (1 bit)
* ? - unknown: always 1 in every packet I've seen (1 bit)
* s - CRC: non-standard CRC-4, reverse-engineered using RevEng (4 bits)
* http://reveng.sourceforge.net
* width=4 poly=0x9 init=0x1 refin=false refout=false xorout=0x0 check=0x5 residue=0x0 name=(none)
*
* Pulse width:
* Short: 2000 us = 0
* Long: 6000 us = 1
* Gap width:
* Short: 6000 us
* Long: 2000 us
* Gap width between packets: 29000 us
*
* Presumably the 4-bit preamble is meant to be a sync of some sort,
* but it has the exact same pulse/gap width as a short pulse, and
* gets processed as data.
*
* Copyright (C) 2017 Chris Coffey (kpuc@sdf.org)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include "rtl_433.h"
#include "util.h"
#define PHILIPS_BITLEN 112
#define PHILIPS_PACKETLEN 4
#define PHILIPS_STARTNIBBLE 0x0
/* Map channel values to their real-world counterparts */
static const uint8_t channel_map[] = { 2, 0, 1, 0, 3 };
/* philips_crc4_bit():
* Compute CRC-4 for a byte sequence, one bit at a time.
*
* Note that Philips uses a custom CRC implementation for this device; see above
* for details.
*
* This function was generated using crcany (https://github.com/madler/crcany)
* License information: "This code is under the zlib license, permitting free
* commercial use."
*/
static unsigned philips_crc4_bit(unsigned crc, void const *mem, size_t len)
{
unsigned char const *data = mem;
if (data == NULL)
return 0x1;
crc <<= 4;
while (len--) {
crc ^= *data++;
for (unsigned k = 0; k < 8; k++)
crc = crc & 0x80 ? (crc << 1) ^ 0x90 : crc << 1;
}
crc >>= 4;
crc &= 0xf;
return crc;
}
/* philips_crc4_rem():
* Compute CRC-4 of the remaining high bits in the low byte of val.
*
* Note that Philips uses a custom CRC implementation for this device; see above
* for details.
*
* This function was generated using crcany (https://github.com/madler/crcany)
* License information: "This code is under the zlib license, permitting free
* commercial use."
*/
static unsigned philips_crc4_rem(unsigned crc, unsigned val, unsigned bits)
{
crc <<= 4;
val &= ((1U << bits) - 1) << (8 - bits);
crc ^= val;
while (bits--)
crc = crc & 0x80 ? (crc << 1) ^ 0x90 : crc << 1;
crc >>= 4;
crc &= 0xf;
return crc;
}
static int philips_callback(bitbuffer_t *bitbuffer)
{
char time_str[LOCAL_TIME_BUFLEN];
uint8_t *bb;
unsigned int i;
uint8_t a, b, c;
uint8_t packet[PHILIPS_PACKETLEN];
uint8_t r_crc, c_crc;
uint8_t channel, battery_status;
int tmp;
float temperature;
data_t *data;
/* Get the time */
local_time_str(0, time_str);
/* Correct number of rows? */
if (bitbuffer->num_rows != 1) {
if (debug_output) {
fprintf(stderr, "%s %s: wrong number of rows (%d)\n",
time_str, __func__, bitbuffer->num_rows);
}
return 0;
}
/* Correct bit length? */
if (bitbuffer->bits_per_row[0] != PHILIPS_BITLEN) {
if (debug_output) {
fprintf(stderr, "%s %s: wrong number of bits (%d)\n",
time_str, __func__, bitbuffer->bits_per_row[0]);
}
return 0;
}
bb = bitbuffer->bb[0];
/* Correct start sequence? */
if ((bb[0] >> 4) != PHILIPS_STARTNIBBLE) {
if (debug_output) {
fprintf(stderr, "%s %s: wrong start nibble\n", time_str, __func__);
}
return 0;
}
/* Compare and combine the 3 repeated packets, with majority wins */
for (i = 0; i < PHILIPS_PACKETLEN; i++) {
a = bb[i+1]; /* First packet - on byte boundary */
b = (bb[i+5] << 4) | (bb[i+6] >> 4 & 0xf); /* Second packet - not on byte boundary */
c = bb[i+10]; /* Third packet - on byte boundary */
packet[i] = (a & b) | (b & c) | (a & c);
}
/* If debug enabled, print the combined majority-wins packet */
if (debug_output) {
fprintf(stderr, "%s %s: combined packet = ", time_str, __func__);
for (i = 0; i < PHILIPS_PACKETLEN; i++) {
fprintf(stderr, "%02x ",packet[i]);
}
fprintf(stderr, "\n");
}
/* Correct CRC? */
r_crc = packet[PHILIPS_PACKETLEN - 1] & 0x0f; /* Last nibble in the packet */
c_crc = philips_crc4_bit(1, packet, PHILIPS_PACKETLEN - 1); /* First three bytes */
c_crc = philips_crc4_rem(c_crc, packet[PHILIPS_PACKETLEN - 1], 4); /* Remaining high nibble */
if (r_crc != c_crc) {
if (debug_output) {
fprintf(stderr, "%s %s: CRC failed, calculated %x, received %x\n",
time_str, __func__, c_crc, r_crc);
}
return 0;
}
/* Message validated, now parse the data */
/* Channel */
channel = packet[0] & 0x0f;
if (channel > (sizeof(channel_map) / sizeof(channel_map[0])))
channel = 0;
else
channel = channel_map[channel];
/* Temperature */
tmp = packet[1];
tmp <<= 2;
tmp |= ((packet[2] & 0xc0) >> 6);
tmp -= 500;
temperature = tmp / 10.0f;
/* Battery status */
battery_status = packet[PHILIPS_PACKETLEN - 1] & 0x40;
data = data_make("time", "", DATA_STRING, time_str,
"model", "", DATA_STRING, "Philips outdoor temperature sensor",
"channel", "Channel", DATA_INT, channel,
"temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature,
"battery", "Battery", DATA_STRING, battery_status ? "LOW" : "OK",
NULL);
data_acquired_handler(data);
return 1;
}
static char *philips_output_fields[] = {
"time",
"model",
"channel",
"temperature_C",
"battery",
NULL
};
r_device philips = {
.name = "Philips outdoor temperature sensor",
.modulation = OOK_PULSE_PWM_TERNARY,
.short_limit = 500,
.long_limit = 4000,
.reset_limit = 30000,
.json_callback = &philips_callback,
.disabled = 0,
.demod_arg = 0,
.fields = philips_output_fields,
};