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