diff --git a/src/devices/x10_rf.c b/src/devices/x10_rf.c index cb53df8f..5c66a87e 100644 --- a/src/devices/x10_rf.c +++ b/src/devices/x10_rf.c @@ -1,12 +1,43 @@ /** @file - X10 sensor (Stub for decoding test data only). + X10 sensor (Non-security devices). Copyright (C) 2015 Tommy Vestermark + Mods. by Dave Fleck 2021 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. + +*/ +/** +X10 sensor decoder. + +Each packet starts with a sync pulse of 9000 us (16x a bit time) +and a 4500 us gap. +The message is OOK PPM encoded with 562.5 us pulse and long gap (0 bit) +of 1687.5 us or short gap (1 bit) of 562.5 us. + +There are 32bits. The message is repeated 5 times with +a packet gap of 40000 us. + +The protocol has a lot of similarities to the NEC IR protocol + +The second byte is the inverse of the first. +The fourth byte is the inverse of the third. + +Based on protocol informtation found at: +http://www.wgldesigns.com/protocols/w800rf32_protocol.txt + +Tested with American sensors operating at 310 MHz +e.g., rtl_433 -f 310M -R 22 + +Seems to work best with 2 MHz sample rate: +rtl_433 -f 310M -R 22 -s 2M + +Tested with HR12A, RMS18, HD23A, MS14A, PMS03, MS12A, +RMS18, Radio Shack 61-2675-T + */ #include "decoder.h" @@ -16,29 +47,38 @@ static int x10_rf_callback(r_device *decoder, bitbuffer_t *bitbuffer) data_t *data; uint8_t *b = bitbuffer->bb[1]; - uint8_t arrbKnownConstBitMask[4] = {0x0B, 0x0B, 0x87, 0x87}; - uint8_t arrbKnownConstBitValue[4] = {0x00, 0x0B, 0x00, 0x87}; - uint8_t bKnownConstFlag = 1; + uint8_t arrbKnownConstBitMask[4] = {0x0B, 0x0B, 0x07, 0x07}; + uint8_t arrbKnownConstBitValue[4] = {0x00, 0x0B, 0x00, 0x07}; // Row [0] is sync pulse - // Validate package - if (bitbuffer->bits_per_row[1] != 32 // Don't waste time on a short package - //|| (b[0] ^ b[1]) != 0xff // Check integrity - apparently some chips may use both bytes.. - || (b[2] ^ b[3]) != 0xff) // Check integrity + // Validate length + if (bitbuffer->bits_per_row[1] != 32) { // Don't waste time on a wrong length package + if (decoder->verbose) + fprintf(stderr, "X10-RF: DECODE_ABORT_LENGTH, Received message length=%i\n", bitbuffer->bits_per_row[1]); return DECODE_ABORT_LENGTH; + } - unsigned code = (unsigned)b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; + // Validate complement values + if ((b[0] ^ b[1]) != 0xff || (b[2] ^ b[3]) != 0xff) { + if (decoder->verbose) + fprintf(stderr, "X10-RF: DECODE_FAIL_SANITY, b0=%02x b1=%02x b2=%02x b3=%02x\n", b[0], b[1], b[2], b[3]); + return DECODE_FAIL_SANITY; + } - // For the CR12A X10 Remote, with the exception of the SCAN buttons, some bits are constant. + // Some bits are constant. for (int8_t bIdx = 0; bIdx < 4; bIdx++) { uint8_t bTest = arrbKnownConstBitMask[bIdx] & b[bIdx]; // Mask the appropriate bits - if (bTest != arrbKnownConstBitValue[bIdx]) // If resulting bits are incorrectly set - bKnownConstFlag = 0; // Set flag to 0, so decoding doesn't occur + if (bTest != arrbKnownConstBitValue[bIdx]) { // If resulting bits are incorrectly set + if (decoder->verbose) + fprintf(stderr, "X10-RF: DECODE_FAIL_SANITY, b0=%02x b1=%02x b2=%02x b3=%02x\n", b[0], b[1], b[2], b[3]); + return DECODE_FAIL_SANITY; + } } - if (bKnownConstFlag != 1) // If constant bits are appropriately set - return DECODE_FAIL_SANITY; + // We have received a valid message, decode it + + unsigned code = (unsigned)b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; uint8_t bHouseCode = 0; uint8_t bDeviceCode = 0; @@ -61,19 +101,53 @@ static int x10_rf_callback(r_device *decoder, bitbuffer_t *bitbuffer) bDeviceCode |= (b[2] & 0x40) >> 4; bDeviceCode |= (b[2] & 0x08) >> 2; bDeviceCode |= (b[2] & 0x10) >> 4; + bDeviceCode += 1; char housecode[2] = {0}; *housecode = bHouseCode + 'A'; int state = (b[2] & 0x20) == 0x00; + char *event_str = "UNKNOWN"; // human-readable event + + if ((b[2] & 0x80) == 0x80) { // Special event bit + bDeviceCode = 0; // No device for special events + + switch (b[2]) { + case 0x98: + event_str = "DIM"; + break; + case 0x88: + event_str = "BRI"; + break; + case 0x90: + event_str = "ALL LTS ON"; + break; + case 0x80: + event_str = "ALL OFF"; + break; + } + } + else { + event_str = state ? "ON" : "OFF"; + } + + // debug output + if (decoder->verbose) { + fprintf(stderr, "X10-RF: id=%s%i event_str=%s\n", housecode, bDeviceCode, event_str); + bitbuffer_print(bitbuffer); + } + + /* clang-format off */ data = data_make( - "model", "", DATA_STRING, "X10-RF", - _X("id", "deviceid"), "", DATA_INT, bDeviceCode + 1, + "model", "", DATA_STRING, "X10-RF", + _X("id", "deviceid"), "", DATA_INT, bDeviceCode, _X("channel", "houseid"), "", DATA_STRING, housecode, - "state", "", DATA_STRING, state ? "ON" : "OFF", - "data", "", DATA_FORMAT, "%08x", DATA_INT, code, + "state", "State", DATA_STRING, event_str, + "data", "Data", DATA_FORMAT, "%08x", DATA_INT, code, + "mic", "Integrity", DATA_STRING, "PARITY", NULL); + /* clang-format on */ decoder_output_data(decoder, data); @@ -88,17 +162,18 @@ static char *output_fields[] = { "deviceid", // TODO: remove ?? "state", "data", + "mic", NULL, }; r_device X10_RF = { .name = "X10 RF", .modulation = OOK_PULSE_PPM, - .short_width = 500, // Short gap 500µs - .long_width = 1680, // Long gap 1680µs - .gap_limit = 2800, // Gap after sync is 4.5ms (1125) + .short_width = 562, // Short gap 562.5 µs + .long_width = 1687, // Long gap 1687.5 µs + .gap_limit = 2200, // Gap after sync is 4.5ms (1125) .reset_limit = 6000, // Gap seen between messages is ~40ms so let's get them individually .decode_fn = &x10_rf_callback, - .disabled = 1, + .disabled = 0, .fields = output_fields, };