ESPHome  2024.4.2
keeloq_protocol.cpp
Go to the documentation of this file.
1 #include "keeloq_protocol.h"
2 #include "esphome/core/log.h"
3 
4 #include <cinttypes>
5 
6 namespace esphome {
7 namespace remote_base {
8 
9 static const char *const TAG = "remote.keeloq";
10 
11 static const uint32_t BIT_TIME_US = 380;
12 static const uint8_t NBITS_PREAMBLE = 12;
13 static const uint8_t NBITS_REPEAT = 1;
14 static const uint8_t NBITS_VLOW = 1;
15 static const uint8_t NBITS_SERIAL = 28;
16 static const uint8_t NBITS_BUTTONS = 4;
17 static const uint8_t NBITS_DISC = 12;
18 static const uint8_t NBITS_SYNC_CNT = 16;
19 
20 static const uint8_t NBITS_FIXED_DATA = NBITS_REPEAT + NBITS_VLOW + NBITS_BUTTONS + NBITS_SERIAL;
21 static const uint8_t NBITS_ENCRYPTED_DATA = NBITS_BUTTONS + NBITS_DISC + NBITS_SYNC_CNT;
22 static const uint8_t NBITS_DATA = NBITS_FIXED_DATA + NBITS_ENCRYPTED_DATA;
23 
24 /*
25 KeeLoq Protocol
26 
27 Coded using information from datasheet for Microchip HCS301 KeeLow Code Hopping Encoder
28 
29 Encoder - Hopping code is generated at random.
30 
31 Decoder - Hopping code is ignored and not checked when received. Serial number of
32 transmitter and nutton command is decoded.
33 
34 */
35 
37  uint32_t out_data = 0x0;
38 
39  ESP_LOGD(TAG, "Send Keeloq: address=%07" PRIx32 " command=%03x encrypted=%08" PRIx32, data.address, data.command,
40  data.encrypted);
41  ESP_LOGV(TAG, "Send Keeloq: data bits (%d + %d)", NBITS_ENCRYPTED_DATA, NBITS_FIXED_DATA);
42 
43  // Preamble = '01' x 12
44  for (uint8_t cnt = NBITS_PREAMBLE; cnt; cnt--) {
45  dst->space(BIT_TIME_US);
46  dst->mark(BIT_TIME_US);
47  }
48 
49  // Header = 10 bit space
50  dst->space(10 * BIT_TIME_US);
51 
52  // Encrypted field
53  out_data = data.encrypted;
54 
55  ESP_LOGV(TAG, "Send Keeloq: Encrypted data %04x", out_data);
56 
57  for (uint32_t mask = 1, cnt = 0; cnt < NBITS_ENCRYPTED_DATA; cnt++, mask <<= 1) {
58  if (out_data & mask) {
59  dst->mark(1 * BIT_TIME_US);
60  dst->space(2 * BIT_TIME_US);
61  } else {
62  dst->mark(2 * BIT_TIME_US);
63  dst->space(1 * BIT_TIME_US);
64  }
65  }
66 
67  // first 32 bits of fixed portion
68  out_data = (data.command & 0x0f);
69  out_data <<= NBITS_SERIAL;
70  out_data |= data.address;
71  ESP_LOGV(TAG, "Send Keeloq: Fixed data %04x", out_data);
72 
73  for (uint32_t mask = 1, cnt = 0; cnt < (NBITS_FIXED_DATA - 2); cnt++, mask <<= 1) {
74  if (out_data & mask) {
75  dst->mark(1 * BIT_TIME_US);
76  dst->space(2 * BIT_TIME_US);
77  } else {
78  dst->mark(2 * BIT_TIME_US);
79  dst->space(1 * BIT_TIME_US);
80  }
81  }
82 
83  // low battery flag
84  if (data.vlow) {
85  dst->mark(1 * BIT_TIME_US);
86  dst->space(2 * BIT_TIME_US);
87  } else {
88  dst->mark(2 * BIT_TIME_US);
89  dst->space(1 * BIT_TIME_US);
90  }
91 
92  // repeat flag - always sent as a '1'
93  dst->mark(1 * BIT_TIME_US);
94  dst->space(2 * BIT_TIME_US);
95 
96  // Guard time at end of packet
97  dst->space(39 * BIT_TIME_US);
98 }
99 
101  KeeloqData out{
102  .encrypted = 0,
103  .address = 0,
104  .command = 0,
105  .repeat = false,
106  .vlow = false,
107 
108  };
109 
110  if (src.size() != (NBITS_PREAMBLE + NBITS_DATA) * 2) {
111  return {};
112  }
113 
114  ESP_LOGVV(TAG, "%2d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", src.size(), src.peek(0),
115  src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6), src.peek(7), src.peek(8),
116  src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14), src.peek(15),
117  src.peek(16), src.peek(17), src.peek(18), src.peek(19));
118 
119  // Check preamble bits
120  int8_t bit = NBITS_PREAMBLE - 1;
121  while (--bit >= 0) {
122  if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(BIT_TIME_US)) {
123  ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %d", bit + 1, src.peek());
124  return {};
125  }
126  }
127  if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(10 * BIT_TIME_US)) {
128  ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %d", bit + 1, src.peek());
129  return {};
130  }
131 
132  // Read encrypted bits
133  uint32_t out_data = 0;
134  for (bit = 0; bit < NBITS_ENCRYPTED_DATA; bit++) {
135  if (src.expect_mark(2 * BIT_TIME_US) && src.expect_space(BIT_TIME_US)) {
136  out_data |= 0 << bit;
137  } else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
138  out_data |= 1 << bit;
139  } else {
140  ESP_LOGV(TAG, "Decode KeeLoq: Fail 2, %d %d", src.get_index(), src.peek());
141  return {};
142  }
143  }
144  ESP_LOGVV(TAG, "Decode KeeLoq: Data, %d %08x", bit, out_data);
145  out.encrypted = out_data;
146 
147  // Read Serial Number and Button Status
148  out_data = 0;
149  for (bit = 0; bit < NBITS_SERIAL + NBITS_BUTTONS; bit++) {
150  if (src.expect_mark(2 * BIT_TIME_US) && src.expect_space(BIT_TIME_US)) {
151  out_data |= 0 << bit;
152  } else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
153  out_data |= 1 << bit;
154  } else {
155  ESP_LOGV(TAG, "Decode KeeLoq: Fail 3, %d %d", src.get_index(), src.peek());
156  return {};
157  }
158  }
159  ESP_LOGVV(TAG, "Decode KeeLoq: Data, %2d %08x", bit, out_data);
160  out.command = (out_data >> 28) & 0xf;
161  out.address = out_data & 0xfffffff;
162 
163  // Read Vlow bit
164  if (src.expect_mark(2 * BIT_TIME_US) && src.expect_space(BIT_TIME_US)) {
165  out.vlow = false;
166  } else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
167  out.vlow = true;
168  } else {
169  ESP_LOGV(TAG, "Decode KeeLoq: Fail 4, %08x", src.peek());
170  return {};
171  }
172 
173  // Read Repeat bit
174  if (src.expect_mark(2 * BIT_TIME_US) && src.peek_space_at_least(BIT_TIME_US)) {
175  out.repeat = false;
176  } else if (src.expect_mark(BIT_TIME_US) && src.peek_space_at_least(2 * BIT_TIME_US)) {
177  out.repeat = true;
178  } else {
179  ESP_LOGV(TAG, "Decode KeeLoq: Fail 5, %08x", src.peek());
180  return {};
181  }
182 
183  return out;
184 }
185 
186 void KeeloqProtocol::dump(const KeeloqData &data) {
187  ESP_LOGD(TAG, "Received Keeloq: address=0x%08" PRIx32 ", command=0x%02x", data.address, data.command);
188 }
189 
190 } // namespace remote_base
191 } // namespace esphome
int32_t peek(uint32_t offset=0) const
Definition: remote_base.h:53
void dump(const KeeloqData &data) override
optional< KeeloqData > decode(RemoteReceiveData src) override
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void encode(RemoteTransmitData *dst, const KeeloqData &data) override
bool peek_space_at_least(uint32_t length, uint32_t offset=0) const
Definition: remote_base.cpp:52