263 lines
9.3 KiB
C
263 lines
9.3 KiB
C
/** @file
|
|
Emos TTX201 Thermo Remote Sensor.
|
|
|
|
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.
|
|
*/
|
|
/**
|
|
Emos TTX201 Thermo Remote Sensor.
|
|
|
|
Manufacturer: Ewig Industries Macao
|
|
Maybe same as Ewig TTX201M (FCC ID: N9ZTTX201M)
|
|
|
|
IROX ETS69 temperature sensor with DCF77 receiver for EBR606C weather station (Ewig WSA101)
|
|
uses the same protocol. It transmits temperature the same way as TTX201 (except for different M bits).
|
|
If its internal clock is synchronized to DCF77, it transmits the date/time every hour (:00) instead of
|
|
the temperature. The date/time is also transmitted after clock is synced at startup.
|
|
|
|
Transmit Interval: every ~61 s
|
|
Frequency: 433.92 MHz
|
|
Manchester Encoding, pulse width: 500 us, interpacket gap width 1500 us.
|
|
|
|
A complete message is 445 bits:
|
|
|
|
PPPPPPPP PPPPPPPP P
|
|
LL LLKKKKKK IIIIIIII S???BCCC ?XXXTTTT TTTTTTTT MMMMMMMM JJJJ (repeated 7 times)
|
|
LL LLKKKKKK IIIIIIII S???BCCC ?XXXTTTT TTTTTTTT MMMMMMMM (last packet without J)
|
|
|
|
17-bit initial preamble, always 0
|
|
|
|
PPPPPPPP PPPPPPPP P = 0x00 0x00 0
|
|
|
|
54-bit data packet format
|
|
|
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 (nibbles #, aligned to 8-bit values)
|
|
..LL LLKKKKKK IIIIIIII StttBCCC 0XXXTTTT TTTTTTTT MMMMMMMM JJJJ (temperature)
|
|
or ..LL LLKKKKKK zyyyyyyy 0tttmmmm dddddHHH HHMMMMMM 0SSSSSS? JJJJ (date/time)
|
|
|
|
- L = 4-bit start of packet, always 0
|
|
- K = 6-bit checksum, sum of nibbles 3-12
|
|
- I = 8-bit sensor ID
|
|
- S = startup (0 = normal operation, 1 = reset or battery changed)
|
|
- t = data type (000 = temperature, 101 = date/time)
|
|
- 0 = unknown, always 0
|
|
- B = battery status (0 = OK, 1 = low)
|
|
- C = 3-bit channel, 0-4
|
|
- X = 3-bit packet index, 0-7
|
|
- T = 12-bit signed temperature * 10 in Celsius
|
|
- M = 8-bit postmark (sensor model?), always 0x14 for TTX201, 0x00 for ETS69
|
|
- J = 4-bit packet separator, always 0xF
|
|
|
|
date/time bit definitions:
|
|
- z = time zone/summer time (0 = CET, 1 = CEST)
|
|
- y = year
|
|
- m = month
|
|
- d = day
|
|
- H = hour
|
|
- M = minute
|
|
- S = second
|
|
- ? = purpose unknown, always 0 or 1, changes only after reset (battery change)
|
|
|
|
Sample received raw data package:
|
|
|
|
bitbuffer:: Number of rows: 10
|
|
[00] {17} 00 00 00 : 00000000 00000000 0
|
|
[01] {54} 07 30 80 00 42 05 3c
|
|
[02] {54} 07 70 80 04 42 05 3c
|
|
[03] {54} 07 b0 80 08 42 05 3c
|
|
[04] {54} 07 f0 80 0c 42 05 3c
|
|
[05] {54} 08 30 80 10 42 05 3c
|
|
[06] {54} 08 70 80 14 42 05 3c
|
|
[07] {54} 08 b0 80 18 42 05 3c
|
|
[08] {50} 08 f0 80 1c 42 05 00 : 00001000 11110000 10000000 00011100 01000010 00000101 00
|
|
[09] { 1} 00 : 0
|
|
|
|
Data decoded:
|
|
|
|
r cs K ID S B C X T M J
|
|
1 28 28 194 0x0 0 0 0 264 0x14 0xf
|
|
2 29 29 194 0x0 0 0 1 264 0x14 0xf
|
|
3 30 30 194 0x0 0 0 2 264 0x14 0xf
|
|
4 31 31 194 0x0 0 0 3 264 0x14 0xf
|
|
5 32 32 194 0x0 0 0 4 264 0x14 0xf
|
|
6 33 33 194 0x0 0 0 5 264 0x14 0xf
|
|
7 34 34 194 0x0 0 0 6 264 0x14 0xf
|
|
8 35 35 194 0x0 0 0 7 264 0x14
|
|
*/
|
|
|
|
#include "decoder.h"
|
|
|
|
#define MSG_PREAMBLE_BITS 17
|
|
#define MSG_PACKET_MIN_BITS 50
|
|
#define MSG_PACKET_BITS 54
|
|
#define MSG_PACKET_POSTMARK 0x14
|
|
#define MSG_MIN_ROWS 2
|
|
#define MSG_MAX_ROWS 10
|
|
|
|
#define MSG_PAD_BITS ((((MSG_PACKET_BITS / 8) + 1) * 8) - MSG_PACKET_BITS)
|
|
#define MSG_PACKET_LEN ((MSG_PACKET_BITS + MSG_PAD_BITS) / 8)
|
|
|
|
#define DATA_TYPE_TEMP 0x00
|
|
#define DATA_TYPE_DATETIME 0x05
|
|
|
|
static int checksum_calculate(uint8_t *b)
|
|
{
|
|
int i;
|
|
int sum = 0;
|
|
|
|
for (i = 1; i < 6; i++) {
|
|
sum += ((b[i] & 0xf0) >> 4) + (b[i] & 0x0f);
|
|
}
|
|
return sum & 0x3f;
|
|
}
|
|
|
|
static int ttx201_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos)
|
|
{
|
|
uint8_t b[MSG_PACKET_LEN];
|
|
int bits = bitbuffer->bits_per_row[row];
|
|
int checksum;
|
|
int checksum_calculated;
|
|
int data_type;
|
|
int postmark;
|
|
int device_id;
|
|
int battery_low;
|
|
int channel;
|
|
int temperature;
|
|
float temperature_c;
|
|
data_t *data;
|
|
|
|
if (bits != MSG_PACKET_MIN_BITS && bits != MSG_PACKET_BITS) {
|
|
if (row == 0) {
|
|
if (bits < MSG_PREAMBLE_BITS) {
|
|
decoder_logf(decoder, 2, __func__, "Short preamble: %d bits (expected %d)",
|
|
bits, MSG_PREAMBLE_BITS);
|
|
}
|
|
} else if (row != (unsigned)bitbuffer->num_rows - 1 && bits == 1) {
|
|
decoder_logf(decoder, 2, __func__, "Wrong packet #%u length: %d bits (expected %d)",
|
|
row, bits, MSG_PACKET_BITS);
|
|
}
|
|
return DECODE_ABORT_LENGTH;
|
|
}
|
|
|
|
bitbuffer_extract_bytes(bitbuffer, row, bitpos + MSG_PAD_BITS, b, MSG_PACKET_BITS + MSG_PAD_BITS);
|
|
|
|
/* Aligned data: LLKKKKKK IIIIIIII S???BCCC ?XXXTTTT TTTTTTTT MMMMMMMM JJJJ */
|
|
checksum = b[0] & 0x3f;
|
|
checksum_calculated = checksum_calculate(b);
|
|
data_type = (b[2] & 0x70) >> 4;
|
|
postmark = b[5];
|
|
|
|
if (decoder_verbose(decoder) > 1) {
|
|
decoder_log(decoder, 0, __func__, "TTX201 received raw data");
|
|
decoder_log_bitbuffer(decoder, 0, __func__, bitbuffer, "");
|
|
decoder_logf(decoder, 0, __func__, "Data decoded:" \
|
|
" r cs K ID S B C X T M J\n");
|
|
decoder_logf(decoder, 0, __func__, "%2u %2d %2d %3d 0x%01x %1d %1d %1d %4d 0x%02x",
|
|
row,
|
|
checksum_calculated,
|
|
checksum,
|
|
b[1], // Device Id
|
|
(b[2] & 0xf0) >> 4, // Unknown 1
|
|
(b[2] & 0x08) >> 3, // Battery
|
|
b[2] & 0x07, // Channel
|
|
b[3] >> 4, // Packet index
|
|
((int8_t)((b[3] & 0x0f) << 4) << 4) | b[4], // Temperature
|
|
postmark);
|
|
if (bits == MSG_PACKET_BITS) {
|
|
decoder_logf(decoder, 0, __func__, " 0x%01x", b[6] >> 4); // Packet separator
|
|
}
|
|
decoder_log(decoder, 0, __func__, "");
|
|
}
|
|
|
|
if (checksum != checksum_calculated) {
|
|
decoder_logf(decoder, 2, __func__, "Packet #%u checksum error.", row);
|
|
return DECODE_FAIL_MIC;
|
|
}
|
|
|
|
if (data_type == DATA_TYPE_DATETIME) {
|
|
int cest = b[1] & 0x80;
|
|
int year = b[1] & 0x7f;
|
|
int month = b[2] & 0x0f;
|
|
int day = (b[3] & 0xf8) >> 3;
|
|
int hour = (b[3] & 0x07) << 2 | (b[4] & 0xc0) >> 6;
|
|
int minute = b[4] & 0x3f;
|
|
int second = (b[5] & 0x7e) >> 1;
|
|
char clock_str[25];
|
|
snprintf(clock_str, sizeof(clock_str), "%04d-%02d-%02dT%02d:%02d:%02d %s", year + 2000, month, day, hour, minute, second, cest ? "CEST" : "CET");
|
|
|
|
/* clang-format off */
|
|
data = data_make(
|
|
"model", "", DATA_STRING, "Emos-TTX201",
|
|
"radio_clock", "Radio Clock", DATA_STRING, clock_str,
|
|
"mic", "Integrity", DATA_STRING, "CHECKSUM",
|
|
NULL);
|
|
/* clang-format on */
|
|
} else { // temperature
|
|
device_id = b[1];
|
|
battery_low = (b[2] & 0x08) != 0; // if not zero, battery is low
|
|
channel = (b[2] & 0x07) + 1;
|
|
temperature = (int16_t)(((b[3] & 0x0f) << 12) | (b[4] << 4)); // uses sign extend
|
|
temperature_c = (temperature >> 4) * 0.1f;
|
|
|
|
/* clang-format off */
|
|
data = data_make(
|
|
"model", "", DATA_STRING, "Emos-TTX201",
|
|
"id", "House Code", DATA_INT, device_id,
|
|
"channel", "Channel", DATA_INT, channel,
|
|
"battery_ok", "Battery", DATA_INT, !battery_low,
|
|
"temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature_c,
|
|
"mic", "Integrity", DATA_STRING, "CHECKSUM",
|
|
NULL);
|
|
/* clang-format on */
|
|
}
|
|
|
|
decoder_output_data(decoder, data);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
Emos TTX201 Thermo Remote Sensor.
|
|
@sa ttx201_decode()
|
|
*/
|
|
static int ttx201_callback(r_device *decoder, bitbuffer_t *bitbuffer)
|
|
{
|
|
int row;
|
|
int ret = 0;
|
|
int events = 0;
|
|
|
|
if (MSG_MIN_ROWS <= bitbuffer->num_rows && bitbuffer->num_rows <= MSG_MAX_ROWS) {
|
|
for (row = 0; row < bitbuffer->num_rows; ++row) {
|
|
ret = ttx201_decode(decoder, bitbuffer, row, 0);
|
|
if (ret > 0)
|
|
events += ret;
|
|
if (events && !decoder_verbose(decoder))
|
|
return events; // for now, break after first successful message
|
|
}
|
|
}
|
|
|
|
return events > 0 ? events : ret;
|
|
}
|
|
|
|
static char const *const output_fields[] = {
|
|
"model",
|
|
"id",
|
|
"channel",
|
|
"battery_ok",
|
|
"temperature_C",
|
|
"mic",
|
|
"radio_clock",
|
|
NULL,
|
|
};
|
|
|
|
r_device const ttx201 = {
|
|
.name = "Emos TTX201 Temperature Sensor",
|
|
.modulation = OOK_PULSE_MANCHESTER_ZEROBIT,
|
|
.short_width = 510,
|
|
.long_width = 0, // not used
|
|
.reset_limit = 1700,
|
|
.tolerance = 250,
|
|
.decode_fn = &ttx201_callback,
|
|
.fields = output_fields,
|
|
};
|