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