Skip to content

Commit

Permalink
Add support for IROX ETS69 to Ewig Emos-TTX201 (#2547)
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrej-zary authored Jul 4, 2023
1 parent a6ea3b0 commit d5ab059
Showing 1 changed file with 62 additions and 25 deletions.
87 changes: 62 additions & 25 deletions src/devices/ttx201.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ 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.
Expand All @@ -29,20 +34,32 @@ A complete message is 445 bits:
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 S???BCCC ?XXXTTTT TTTTTTTT MMMMMMMM JJJJ
..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)
- ? = unknown, always 0
- 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, always 0x14
- 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
Expand Down Expand Up @@ -82,6 +99,9 @@ Data decoded:
#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;
Expand All @@ -99,6 +119,7 @@ static int ttx201_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row
int bits = bitbuffer->bits_per_row[row];
int checksum;
int checksum_calculated;
int data_type;
int postmark;
int device_id;
int battery_low;
Expand Down Expand Up @@ -127,6 +148,7 @@ static int ttx201_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row
/* 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 > 1) {
Expand All @@ -151,33 +173,47 @@ static int ttx201_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row
decoder_log(decoder, 0, __func__, "");
}

if (postmark != MSG_PACKET_POSTMARK) {
decoder_logf(decoder, 2, __func__, "Packet #%u wrong postmark 0x%02x (expected 0x%02x).",
row, postmark, MSG_PACKET_POSTMARK);
return DECODE_FAIL_SANITY;
}

if (checksum != checksum_calculated) {
decoder_logf(decoder, 2, __func__, "Packet #%u checksum error.", row);
return DECODE_FAIL_MIC;
}

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 */
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];
sprintf(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;
Expand Down Expand Up @@ -213,6 +249,7 @@ static char const *const output_fields[] = {
"battery_ok",
"temperature_C",
"mic",
"radio_clock",
NULL,
};

Expand Down

0 comments on commit d5ab059

Please sign in to comment.