1 /** @file
2     X10 sensor (Non-security devices).
3 
4     Copyright (C) 2015 Tommy Vestermark
5     Mods. by Dave Fleck 2021
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12 */
13 /**
14 X10  sensor decoder.
15 
16 Each packet starts with a sync pulse of 9000 us (16x a bit time)
17 and a 4500 us gap.
18 The message is OOK PPM encoded with 562.5 us pulse and long gap (0 bit)
19 of 1687.5 us or short gap (1 bit) of 562.5 us.
20 
21 There are 32bits. The message is repeated 5 times with
22 a packet gap of 40000 us.
23 
24 The protocol has a lot of similarities to the NEC IR protocol
25 
26 The second byte is the inverse of the first.
27 The fourth byte is the inverse of the third.
28 
29 Based on protocol informtation found at:
30 http://www.wgldesigns.com/protocols/w800rf32_protocol.txt
31 
32 Tested with American sensors operating at 310 MHz
33 e.g., rtl_433 -f 310M -R 22
34 
35 Seems to work best with 2 MHz sample rate:
36 rtl_433 -f 310M -R 22 -s 2M
37 
38 Tested with HR12A, RMS18, HD23A, MS14A, PMS03, MS12A,
39 RMS18, Radio Shack 61-2675-T
40 
41 */
42 
43 #include "decoder.h"
44 
x10_rf_callback(r_device * decoder,bitbuffer_t * bitbuffer)45 static int x10_rf_callback(r_device *decoder, bitbuffer_t *bitbuffer)
46 {
47     data_t *data;
48     uint8_t *b = bitbuffer->bb[1];
49 
50     uint8_t arrbKnownConstBitMask[4]  = {0x0B, 0x0B, 0x07, 0x07};
51     uint8_t arrbKnownConstBitValue[4] = {0x00, 0x0B, 0x00, 0x07};
52 
53     // Row [0] is sync pulse
54     // Validate length
55     if (bitbuffer->bits_per_row[1] != 32) { // Don't waste time on a wrong length package
56         if (decoder->verbose && bitbuffer->bits_per_row[1] != 0)
57             fprintf(stderr, "X10-RF: DECODE_ABORT_LENGTH, Received message length=%i\n", bitbuffer->bits_per_row[1]);
58         return DECODE_ABORT_LENGTH;
59     }
60 
61     // Validate complement values
62     if ((b[0] ^ b[1]) != 0xff || (b[2] ^ b[3]) != 0xff) {
63         if (decoder->verbose)
64             fprintf(stderr, "X10-RF: DECODE_FAIL_SANITY, b0=%02x b1=%02x b2=%02x b3=%02x\n", b[0], b[1], b[2], b[3]);
65         return DECODE_FAIL_SANITY;
66     }
67 
68     // Some bits are constant.
69     for (int8_t bIdx = 0; bIdx < 4; bIdx++) {
70         uint8_t bTest = arrbKnownConstBitMask[bIdx] & b[bIdx];  // Mask the appropriate bits
71 
72         if (bTest != arrbKnownConstBitValue[bIdx]) {  // If resulting bits are incorrectly set
73             if (decoder->verbose)
74                 fprintf(stderr, "X10-RF: DECODE_FAIL_SANITY, b0=%02x b1=%02x b2=%02x b3=%02x\n", b[0], b[1], b[2], b[3]);
75             return DECODE_FAIL_SANITY;
76         }
77     }
78 
79     // We have received a valid message, decode it
80 
81     unsigned code = (unsigned)b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3];
82 
83     uint8_t bHouseCode  = 0;
84     uint8_t bDeviceCode = 0;
85     uint8_t arrbHouseBits[4] = {0, 0, 0, 0};
86 
87     // Extract House bits
88     arrbHouseBits[0] = (b[0] & 0x80) >> 7;
89     arrbHouseBits[1] = (b[0] & 0x40) >> 6;
90     arrbHouseBits[2] = (b[0] & 0x20) >> 5;
91     arrbHouseBits[3] = (b[0] & 0x10) >> 4;
92 
93     // Convert bits into integer
94     bHouseCode   = (~(arrbHouseBits[0] ^ arrbHouseBits[1])  & 0x01) << 3;
95     bHouseCode  |= ( ~arrbHouseBits[1]                      & 0x01) << 2;
96     bHouseCode  |= ( (arrbHouseBits[1] ^ arrbHouseBits[2])  & 0x01) << 1;
97     bHouseCode  |=    arrbHouseBits[3]                      & 0x01;
98 
99     // Extract and convert Unit bits to integer
100     bDeviceCode  = (b[0] & 0x04) << 1;
101     bDeviceCode |= (b[2] & 0x40) >> 4;
102     bDeviceCode |= (b[2] & 0x08) >> 2;
103     bDeviceCode |= (b[2] & 0x10) >> 4;
104     bDeviceCode += 1;
105 
106     char housecode[2] = {0};
107     *housecode = bHouseCode + 'A';
108 
109     int state = (b[2] & 0x20) == 0x00;
110 
111     char *event_str = "UNKNOWN";         // human-readable event
112 
113     if ((b[2] & 0x80) == 0x80) {         // Special event bit
114         bDeviceCode = 0;                 // No device for special events
115 
116         switch (b[2]) {
117             case 0x98:
118                 event_str = "DIM";
119                 break;
120             case 0x88:
121                 event_str = "BRI";
122                 break;
123             case 0x90:
124                 event_str = "ALL LTS ON";
125                 break;
126             case 0x80:
127                 event_str = "ALL OFF";
128                 break;
129         }
130     }
131     else {
132         event_str = state ? "ON" : "OFF";
133     }
134 
135     // debug output
136     if (decoder->verbose) {
137         fprintf(stderr, "X10-RF: id=%s%i event_str=%s\n", housecode, bDeviceCode, event_str);
138         bitbuffer_print(bitbuffer);
139     }
140 
141     /* clang-format off */
142     data = data_make(
143             "model",        "",             DATA_STRING, "X10-RF",
144             "id",           "",             DATA_INT,    bDeviceCode,
145             "channel",      "",             DATA_STRING, housecode,
146             "state",        "State",        DATA_STRING, event_str,
147             "data",         "Data",         DATA_FORMAT, "%08x", DATA_INT, code,
148             "mic",          "Integrity",    DATA_STRING, "PARITY",
149             NULL);
150     /* clang-format on */
151 
152     decoder_output_data(decoder, data);
153 
154     return 1;
155 }
156 
157 static char *output_fields[] = {
158         "model",
159         "channel",
160         "id",
161         "state",
162         "data",
163         "mic",
164         NULL,
165 };
166 
167 r_device X10_RF = {
168         .name        = "X10 RF",
169         .modulation  = OOK_PULSE_PPM,
170         .short_width = 562,  // Short gap 562.5 µs
171         .long_width  = 1687, // Long gap 1687.5 µs
172         .gap_limit   = 2200, // Gap after sync is 4.5ms (1125)
173         .reset_limit = 6000, // Gap seen between messages is ~40ms so let's get them individually
174         .decode_fn   = &x10_rf_callback,
175         .disabled    = 0,
176         .fields      = output_fields,
177 };
178