|
| 1 | +/** @file |
| 2 | + Interlogix/GE/UTC Wireless Device Decoder. |
| 3 | +
|
| 4 | + Copyright (C) 2017 Brent Bailey <bailey.brent@gmail.com> |
| 5 | +
|
| 6 | + This program is free software; you can redistribute it and/or modify |
| 7 | + it under the terms of the GNU General Public License as published by |
| 8 | + the Free Software Foundation; either version 2 of the License, or |
| 9 | + (at your option) any later version. |
| 10 | +*/ |
| 11 | +/** |
| 12 | +Interlogix/GE/UTC Wireless Device Decoder. |
| 13 | +
|
| 14 | +Also tested with ELK-319DWM module as well as a Alula RE101 319.5MHz sensor (both short preamble). |
| 15 | +
|
| 16 | +- Frequency: 319.5 MHz |
| 17 | +
|
| 18 | +Decoding done per us patent #5761206 |
| 19 | +https://www.google.com/patents/US5761206 |
| 20 | +
|
| 21 | +## Protocol Bits |
| 22 | +
|
| 23 | +- 00-02 976 uS RF front porch pulse |
| 24 | +- 03-14 12 sync pulses, logical zeros |
| 25 | +- 15 start pulse, logical one |
| 26 | +- 16-35 20 bit sensor identification code (ID bits 0-19) |
| 27 | +- 36-39 4 bit device type code (DT bits 0-3) |
| 28 | +- 40-42 3 bit trigger count (TC bit 0-2) |
| 29 | +- 43 low battery bit |
| 30 | +- 44 F1 latch bit NOTE that F1 latch bit and debounce are reversed. Typo or endianness issue? |
| 31 | +- 45 F1 debounced level |
| 32 | +- 46 F2 latch bit |
| 33 | +- 47 F2 debounced level |
| 34 | +- 48 F3 latch bit (cover latch for contact sensors) |
| 35 | +- 49 F3 debounced level |
| 36 | +- 50 F4 latch bit |
| 37 | +- 51 F4 debounced level |
| 38 | +- 52 F5 positive latch bit |
| 39 | +- 53 F5 debounced level |
| 40 | +- 54 F5 negative latch bit |
| 41 | +- 55 even parity over odd bits 15-55 |
| 42 | +- 56 odd parity over even bits 16-56 |
| 43 | +- 57 zero/one, programmable |
| 44 | +- 58 RF on for 366 uS (old stop bit) |
| 45 | +- 59 one |
| 46 | +- 60-62 modulus 8 count of number of ones in bits 15-54 |
| 47 | +- 63 zero (new stop bit) |
| 48 | +
|
| 49 | +## Protocol Description |
| 50 | +
|
| 51 | +- Bits 00 to 02 are a 976 ms RF front porch pulse, providing a wake up period that allows the |
| 52 | + system controller receiver to synchronize with the incoming packet. |
| 53 | +- Bits 3 to 14 include 12 sync pulses, e.g., logical 0's, to synchronize the receiver. |
| 54 | +- Bit 15 is a start pulse, e.g., a logical 1, that tells the receiver that data is to follow. |
| 55 | +- Bits 16-58 provide information regarding the transmitter and associated sensor. In other |
| 56 | + embodiments, bits 16-58 may be replaced by an analog signal. |
| 57 | +- Bits 16 to 35 provide a 20-bit sensor identification code that uniquely identifies the particular |
| 58 | + sensor sending the message. Bits 36 to 39 provide a 4 bit device-type code that identifies the |
| 59 | + specific-type of sensor, e.g., smoke, PIR, door, window, etc. The combination of the sensor |
| 60 | + bits and device bits provide a set of data bits. |
| 61 | +- Bits 40 through 42 provide a 3-bit trigger count that is incremented for each group of message |
| 62 | + packets. The trigger count is a simple but effective way for preventing a third party from |
| 63 | + recording a message packet transmission and then re-transmitting that message packet |
| 64 | + transmission to make the system controller think that a valid message packet is being transmitted. |
| 65 | +- Bit 43 provides the low battery bit. |
| 66 | +- Bits 44 through 53 provide the latch bit value and the debounced value for each of the five inputs |
| 67 | + associated with the transmitter. For the F5 input, both a positive and negative latch bit are provided. |
| 68 | +- Bit 55 provides even parity over odd bits 15 to 55. |
| 69 | +- Bit 56 provides odd parity over even bits 16 to 56. |
| 70 | +- Bit 57 is a programmable bit that can be used for a variety of applications, including providing an |
| 71 | + additional bit that could be used for the sensor identification code or device type code. |
| 72 | +- Bit 58 is a 366 ms RF on signal that functions as the "old" stop bit. This bit provides compatibility with |
| 73 | + prior system controllers that may be programmed to receive a 58-bit message. |
| 74 | +- Bit 59 is a logical 1. |
| 75 | +- Bits 60 to 62 are a modulus eight count of the number of 1 bits in bits 15 through 54, providing enhanced |
| 76 | + error detection information to be used by the system controller. Finally, bit 63 is the "new" stop bit, |
| 77 | + e.g., a logical 0, that tells the system controller that it is the end of the message packet. |
| 78 | +
|
| 79 | +## Addendum |
| 80 | +
|
| 81 | +GE/Interlogix keyfobs do not follow the documented iti protocol and it |
| 82 | +appears the protocol was misread by the team that created the keyfobs. |
| 83 | +The button states are sent in the three trigger count bits (bit 40-42) |
| 84 | +and no battery status appears to be provided. 4 buttons and a single |
| 85 | +multi-button press (buttons 1 - lock and buttons 2 - unlock) for a total |
| 86 | +of 5 buttons available on the keyfob. |
| 87 | +
|
| 88 | +For contact sensors, latch 3 (typically the tamper/case open latch) will |
| 89 | +float (giving misreads) if the external contacts are used (ie; closed) |
| 90 | +and there is no 4.7 Kohm end of line resistor in place on the external |
| 91 | +circuit |
| 92 | +*/ |
| 93 | + |
| 94 | +#include "decoder.h" |
| 95 | + |
| 96 | +#define INTERLOGIX_MSG_BIT_LEN 46 |
| 97 | + |
| 98 | +static int interlogix_decode(r_device *decoder, bitbuffer_t *bitbuffer) |
| 99 | +{ |
| 100 | + // preamble message |
| 101 | + // only searching for 0000 0001 (bottom 8 bits of the 13 bits preamble) |
| 102 | + uint8_t const preamble_pattern[1] = {0x01}; // 8 bits |
| 103 | + |
| 104 | + data_t *data; |
| 105 | + unsigned int row = 0; |
| 106 | + char device_type_id[2]; |
| 107 | + char const *device_type; |
| 108 | + char device_serial[7]; |
| 109 | + char raw_message[7]; |
| 110 | + int low_battery; |
| 111 | + char const *f1_latch_state; |
| 112 | + char const *f2_latch_state; |
| 113 | + char const *f3_latch_state; |
| 114 | + char const *f4_latch_state; |
| 115 | + char const *f5_latch_state; |
| 116 | + |
| 117 | + if (bitbuffer->num_rows != 1) { |
| 118 | + return DECODE_ABORT_EARLY; |
| 119 | + } |
| 120 | + |
| 121 | + // Check if the message length is between the length seen in test files (57) |
| 122 | + // and the 64 bits discussed above. |
| 123 | + if (bitbuffer->bits_per_row[0] < 57 |
| 124 | + || bitbuffer->bits_per_row[0] > 64) { |
| 125 | + return DECODE_ABORT_LENGTH; |
| 126 | + } |
| 127 | + |
| 128 | + // search for preamble and exit if not found |
| 129 | + unsigned int bit_offset = bitbuffer_search(bitbuffer, row, 0, preamble_pattern, (sizeof preamble_pattern) * 8); |
| 130 | + if (bit_offset == bitbuffer->bits_per_row[row]) { |
| 131 | + decoder_logf(decoder, 2, __func__, "Preamble not found, bit_offset: %u", bit_offset); |
| 132 | + return DECODE_FAIL_SANITY; |
| 133 | + } |
| 134 | + |
| 135 | + // set message starting position (just past preamble and sync bit) |
| 136 | + bit_offset += (sizeof preamble_pattern) * 8; |
| 137 | + |
| 138 | + uint8_t message[(INTERLOGIX_MSG_BIT_LEN + 7) / 8]; |
| 139 | + |
| 140 | + bitbuffer_extract_bytes(bitbuffer, row, bit_offset, message, INTERLOGIX_MSG_BIT_LEN); |
| 141 | + |
| 142 | + // reduce false positives, abort if id or code looks wrong |
| 143 | + if (message[0] == 0x00 && message[1] == 0x00 && message[2] == 0x00) |
| 144 | + return DECODE_FAIL_SANITY; |
| 145 | + if (message[0] == 0xff && message[1] == 0xff && message[2] == 0xff) |
| 146 | + return DECODE_FAIL_SANITY; |
| 147 | + if (message[3] == 0x00 && message[4] == 0x00 && message[5] == 0x00) |
| 148 | + return DECODE_FAIL_SANITY; |
| 149 | + if (message[3] == 0xff && message[4] == 0xff && message[5] == 0xff) |
| 150 | + return DECODE_FAIL_SANITY; |
| 151 | + |
| 152 | + // parity check: even data bits from message[0 .. 40] and odd data bits from message[1 .. 41] |
| 153 | + // i.e. 5 bytes and two (top-most) bits. |
| 154 | + int parity = message[0] ^ message[1] ^ message[2] ^ message[3] ^ message[4]; // parity as byte |
| 155 | + parity = (parity >> 4) ^ (parity & 0xF); // fold to nibble |
| 156 | + parity = (parity >> 2) ^ (parity & 0x3); // fold to 2 bits |
| 157 | + parity ^= message[5] >> 6; // add check bits |
| 158 | + int parity_error = parity ^ 0x3; // both parities are odd, i.e. 1 on success |
| 159 | + |
| 160 | + if (parity_error) { |
| 161 | + decoder_logf(decoder, 1, __func__, "Parity check failed (%d %d)", parity >> 1, parity & 1); |
| 162 | + return DECODE_FAIL_MIC; |
| 163 | + } |
| 164 | + |
| 165 | + sprintf(device_type_id, "%01x", (reverse8(message[2]) >> 4)); |
| 166 | + |
| 167 | + switch ((reverse8(message[2]) >> 4)) { |
| 168 | + case 0xa: device_type = "contact"; break; |
| 169 | + case 0xf: device_type = "keyfob"; break; |
| 170 | + case 0x4: device_type = "motion"; break; |
| 171 | + case 0x6: device_type = "heat"; break; |
| 172 | + case 0x9: device_type = "glass"; break; // switch1 changes from open to closed on trigger |
| 173 | + |
| 174 | + default: device_type = "unknown"; return DECODE_FAIL_SANITY; break; |
| 175 | + } |
| 176 | + |
| 177 | + sprintf(device_serial, "%02x%02x%02x", reverse8(message[2]), reverse8(message[1]), reverse8(message[0])); |
| 178 | + |
| 179 | + sprintf(raw_message, "%02x%02x%02x", message[3], message[4], message[5]); |
| 180 | + |
| 181 | + // keyfob logic. see protocol description addendum for protocol exceptions |
| 182 | + if ((reverse8(message[2]) >> 4) == 0xf) { |
| 183 | + low_battery = 0; |
| 184 | + f1_latch_state = ((message[3] & 0xe) == 0x4) ? "CLOSED" : "OPEN"; |
| 185 | + f2_latch_state = ((message[3] & 0xe) == 0x8) ? "CLOSED" : "OPEN"; |
| 186 | + f3_latch_state = ((message[3] & 0xe) == 0xc) ? "CLOSED" : "OPEN"; |
| 187 | + f4_latch_state = ((message[3] & 0xe) == 0x2) ? "CLOSED" : "OPEN"; |
| 188 | + f5_latch_state = ((message[3] & 0xe) == 0xa) ? "CLOSED" : "OPEN"; |
| 189 | + } else { |
| 190 | + low_battery = (message[3] & 0x10) ? 1 : 0; |
| 191 | + f1_latch_state = (message[3] & 0x04) ? "OPEN" : "CLOSED"; |
| 192 | + f2_latch_state = (message[3] & 0x01) ? "OPEN" : "CLOSED"; |
| 193 | + f3_latch_state = (message[4] & 0x40) ? "OPEN" : "CLOSED"; |
| 194 | + f4_latch_state = (message[4] & 0x10) ? "OPEN" : "CLOSED"; |
| 195 | + f5_latch_state = (message[4] & 0x04) ? "OPEN" : "CLOSED"; |
| 196 | + } |
| 197 | + |
| 198 | + /* clang-format off */ |
| 199 | + data = data_make( |
| 200 | + "model", "Model", DATA_STRING, "Interlogix-Security", |
| 201 | + "subtype", "Device Type", DATA_STRING, device_type, |
| 202 | + "id", "ID", DATA_STRING, device_serial, |
| 203 | + "battery_ok", "Battery", DATA_INT, !low_battery, |
| 204 | + "switch1", "Switch1 State", DATA_STRING, f1_latch_state, |
| 205 | + "switch2", "Switch2 State", DATA_STRING, f2_latch_state, |
| 206 | + "switch3", "Switch3 State", DATA_STRING, f3_latch_state, |
| 207 | + "switch4", "Switch4 State", DATA_STRING, f4_latch_state, |
| 208 | + "switch5", "Switch5 State", DATA_STRING, f5_latch_state, |
| 209 | + "raw_message", "Raw Message", DATA_STRING, raw_message, |
| 210 | + NULL); |
| 211 | + /* clang-format on */ |
| 212 | + |
| 213 | + decoder_output_data(decoder, data); |
| 214 | + return 1; |
| 215 | +} |
| 216 | + |
| 217 | +static char const *const output_fields[] = { |
| 218 | + "model", |
| 219 | + "subtype", |
| 220 | + "id", |
| 221 | + "raw_message", |
| 222 | + "battery_ok", |
| 223 | + "switch1", |
| 224 | + "switch2", |
| 225 | + "switch3", |
| 226 | + "switch4", |
| 227 | + "switch5", |
| 228 | + NULL, |
| 229 | +}; |
| 230 | + |
| 231 | +r_device const interlogix = { |
| 232 | + .name = "Interlogix GE UTC Security Devices", |
| 233 | + .modulation = OOK_PULSE_PPM, |
| 234 | + .short_width = 122, |
| 235 | + .long_width = 244, |
| 236 | + .reset_limit = 500, // Maximum gap size before End Of Message |
| 237 | + .decode_fn = &interlogix_decode, |
| 238 | + .fields = output_fields, |
| 239 | +}; |
0 commit comments