ESPHome 2025.5.0
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 {
7namespace remote_base {
8
9static const char *const TAG = "remote.keeloq";
10
11static const uint32_t BIT_TIME_US = 380;
12static const uint8_t NBITS_PREAMBLE = 12;
13static const uint8_t NBITS_REPEAT = 1;
14static const uint8_t NBITS_VLOW = 1;
15static const uint8_t NBITS_SERIAL = 28;
16static const uint8_t NBITS_BUTTONS = 4;
17static const uint8_t NBITS_DISC = 12;
18static const uint8_t NBITS_SYNC_CNT = 16;
19
20static const uint8_t NBITS_FIXED_DATA = NBITS_REPEAT + NBITS_VLOW + NBITS_BUTTONS + NBITS_SERIAL;
21static const uint8_t NBITS_ENCRYPTED_DATA = NBITS_BUTTONS + NBITS_DISC + NBITS_SYNC_CNT;
22static const uint8_t NBITS_DATA = NBITS_FIXED_DATA + NBITS_ENCRYPTED_DATA;
23
24/*
25KeeLoq Protocol
26
27Coded using information from datasheet for Microchip HCS301 KeeLow Code Hopping Encoder
28
29Encoder - Hopping code is generated at random.
30
31Decoder - Hopping code is ignored and not checked when received. Serial number of
32transmitter 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 %04" PRIx32, 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 %04" PRIx32, 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,
115 "%2" PRId32 ": %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
116 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
117 " %" PRId32 " %" PRId32 " %" PRId32,
118 src.size(), src.peek(0), src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6),
119 src.peek(7), src.peek(8), src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14),
120 src.peek(15), src.peek(16), src.peek(17), src.peek(18), src.peek(19));
121
122 // Check preamble bits
123 int8_t bit = NBITS_PREAMBLE - 1;
124 while (--bit >= 0) {
125 if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(BIT_TIME_US)) {
126 ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %" PRId32, bit + 1, src.peek());
127 return {};
128 }
129 }
130 if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(10 * BIT_TIME_US)) {
131 ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %" PRId32, bit + 1, src.peek());
132 return {};
133 }
134
135 // Read encrypted bits
136 uint32_t out_data = 0;
137 for (bit = 0; bit < NBITS_ENCRYPTED_DATA; bit++) {
138 if (src.expect_mark(2 * BIT_TIME_US) && src.expect_space(BIT_TIME_US)) {
139 out_data |= 0 << bit;
140 } else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
141 out_data |= 1 << bit;
142 } else {
143 ESP_LOGV(TAG, "Decode KeeLoq: Fail 2, %" PRIu32 " %" PRId32, src.get_index(), src.peek());
144 return {};
145 }
146 }
147 ESP_LOGVV(TAG, "Decode KeeLoq: Data, %d %08" PRIx32, bit, out_data);
148 out.encrypted = out_data;
149
150 // Read Serial Number and Button Status
151 out_data = 0;
152 for (bit = 0; bit < NBITS_SERIAL + NBITS_BUTTONS; bit++) {
153 if (src.expect_mark(2 * BIT_TIME_US) && src.expect_space(BIT_TIME_US)) {
154 out_data |= 0 << bit;
155 } else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
156 out_data |= 1 << bit;
157 } else {
158 ESP_LOGV(TAG, "Decode KeeLoq: Fail 3, %" PRIu32 " %" PRId32, src.get_index(), src.peek());
159 return {};
160 }
161 }
162 ESP_LOGVV(TAG, "Decode KeeLoq: Data, %2d %08" PRIx32, bit, out_data);
163 out.command = (out_data >> 28) & 0xf;
164 out.address = out_data & 0xfffffff;
165
166 // Read Vlow bit
167 if (src.expect_mark(2 * BIT_TIME_US) && src.expect_space(BIT_TIME_US)) {
168 out.vlow = false;
169 } else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
170 out.vlow = true;
171 } else {
172 ESP_LOGV(TAG, "Decode KeeLoq: Fail 4, %" PRId32, src.peek());
173 return {};
174 }
175
176 // Read Repeat bit
177 if (src.expect_mark(2 * BIT_TIME_US) && src.peek_space_at_least(BIT_TIME_US)) {
178 out.repeat = false;
179 } else if (src.expect_mark(BIT_TIME_US) && src.peek_space_at_least(2 * BIT_TIME_US)) {
180 out.repeat = true;
181 } else {
182 ESP_LOGV(TAG, "Decode KeeLoq: Fail 5, %" PRId32, src.peek());
183 return {};
184 }
185
186 return out;
187}
188
190 ESP_LOGD(TAG, "Received Keeloq: address=0x%08" PRIx32 ", command=0x%02x", data.address, data.command);
191}
192
193} // namespace remote_base
194} // namespace esphome
void dump(const KeeloqData &data) override
void encode(RemoteTransmitData *dst, const KeeloqData &data) override
optional< KeeloqData > decode(RemoteReceiveData src) override
int32_t peek(uint32_t offset=0) const
Definition remote_base.h:58
bool peek_space_at_least(uint32_t length, uint32_t offset=0) const
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7