summaryrefslogtreecommitdiffstats
path: root/martin/door/src/hcs200.cpp
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2020-03-17 12:18:56 +0100
committermanuel <manuel@mausz.at>2020-03-17 12:18:56 +0100
commit0553b9907f912e56a2cd2a08b03a83ce6f2a75c6 (patch)
tree45ed1908a50168f8420f13866cfd68386d0f2c78 /martin/door/src/hcs200.cpp
parentf0ecb4d38fff522c72905a8551355ca925381fa3 (diff)
downloadarduino-0553b9907f912e56a2cd2a08b03a83ce6f2a75c6.tar.gz
arduino-0553b9907f912e56a2cd2a08b03a83ce6f2a75c6.tar.bz2
arduino-0553b9907f912e56a2cd2a08b03a83ce6f2a75c6.zip
Add martin/door
Diffstat (limited to 'martin/door/src/hcs200.cpp')
-rw-r--r--martin/door/src/hcs200.cpp242
1 files changed, 242 insertions, 0 deletions
diff --git a/martin/door/src/hcs200.cpp b/martin/door/src/hcs200.cpp
new file mode 100644
index 0000000..401e670
--- /dev/null
+++ b/martin/door/src/hcs200.cpp
@@ -0,0 +1,242 @@
1#include <Arduino.h>
2
3#include "hcs200.h"
4
5/** Pulse width lengths used by the transmitter. A pulse width is the time
6 * between a transition from 0 to 1 or from 1 to 0 of the input pin.
7 */
8enum RbType
9{
10 RB_SHORT = 0, // pulse_width
11 RB_LONG, // 2 * pulse_width
12 RB_ERROR
13};
14
15/** Determine if 'pulse' is a short pulse, RB_SHORT or a long one, RB_LONG.
16 * Normally, a short pulse is the same as the 'tx_clock' and a long pulse is
17 * twice as long. However, timing problems with servicing interrupts means
18 * that we need to add some fudge factors.
19 */
20int HCS200::classify(unsigned pulse)
21{
22 int d = pulse - tx_clock;
23 if (d < -100)
24 return RB_ERROR;
25 else if (d < 100)
26 return RB_SHORT;
27 else
28 {
29 d -= tx_clock;
30 if (d < -100)
31 return RB_ERROR;
32 else if (d < 100)
33 return RB_LONG;
34 else
35 return RB_ERROR;
36 }
37}
38
39#define IN_RANGE(x, min, max) (x >= min && x <= max)
40void HCS200::on_isr(uint8_t value)
41{
42 unsigned long timestamp = micros();
43 unsigned long pulse_width = timestamp - last_timestamp;
44 int d;
45
46 switch (rx_state) {
47 case RS_PREAMBLE_START:
48 // value == 0
49 preamble_count = 1;
50 rx_state = RS_PREAMBLE_LOW;
51 tx_clock = pulse_width;
52 break;
53 case RS_PREAMBLE_LOW:
54 // value == 1
55 preamble_count++;
56 d = pulse_width - tx_clock;
57 rx_state = (IN_RANGE(d, -100, 100)) ? RS_PREAMBLE_HIGH : RS_NOSYNC;
58 break;
59 case RS_PREAMBLE_HIGH:
60 // value == 0
61 preamble_count++;
62 d = pulse_width - tx_clock;
63 if (IN_RANGE(d, -100, 100))
64 rx_state = (preamble_count == 23) ? RS_PREAMBLE_HEADER : RS_PREAMBLE_LOW;
65 else
66 rx_state = RS_NOSYNC;
67 break;
68 case RS_PREAMBLE_HEADER:
69 // header should be low for 10*tx_clock
70 d = pulse_width - 10 * tx_clock;
71 if (value == 1 && IN_RANGE(d, -100, 100))
72 {
73 rx_state = RS_DATA;
74 rx_bit_count = 0;
75 memset(rx_buf, 0, sizeof(rx_buf));
76 }
77 else
78 rx_state = RS_NOSYNC;
79 break;
80 case RS_DATA:
81 if (value == 1)
82 {
83 int first = classify(last_pulse_width);
84 int second = classify(pulse_width);
85 // received a 1 bit
86 if (first == RB_SHORT && second == RB_LONG)
87 {
88 int idx = rx_bit_count / 32;
89 rx_buf[idx] >>= 1;
90 rx_buf[idx] |= 0x80000000;
91 rx_bit_count++;
92 }
93 // received a 0 bit
94 else if (first == RB_LONG && second == RB_SHORT)
95 {
96 int idx = rx_bit_count / 32;
97 rx_buf[idx] >>= 1;
98 rx_bit_count++;
99 }
100 else
101 rx_state = RS_NOSYNC;
102
103 // we ignore the last bit as it's always "1"
104 // instead we use the raising edge as trigger to stop
105 if (rx_bit_count == MAX_BITS - 1)
106 rx_state = RS_COMPLETED;
107 }
108 break;
109 }
110
111 // check outside of the state machine
112 // this is important as otherwise otherwise we always miss the start
113 if (rx_state == RS_NOSYNC && value == 1)
114 rx_state = RS_PREAMBLE_START;
115
116 last_timestamp = timestamp;
117 last_pulse_width = pulse_width;
118}
119
120void HCS200::reset()
121{
122 rx_state = RS_NOSYNC;
123}
124
125bool HCS200::decode(HCS200_Keycode &out)
126{
127 if (rx_state != RS_COMPLETED)
128 return false;
129 out.encrypted = rx_buf[0];
130 out.serial = rx_buf[1] & 0x0FFFFFFF;
131 out.buttons = (rx_buf[1] >> 28) & 0xF;
132 out.lowbat = rx_buf[2] & 0x80000000;
133 return true;
134}
135
136void HCS200::print_state(Print &stream)
137{
138 stream.print("rx_state=");
139 stream.print(rx_state, DEC);
140 stream.print(", rx_bit_count=");
141 Serial.print(rx_bit_count, DEC);
142 stream.print(", tx_clock=");
143 stream.print(tx_clock, DEC);
144 stream.print(", preamble_count=");
145 stream.println(preamble_count, DEC);
146}
147
148void HCS200_Keycode::print(Print &stream)
149{
150 stream.print("Keyfob# ");
151 stream.print(serial, HEX);
152 stream.print(", buttons:");
153 if (buttons & BM_S0)
154 stream.print(" 1");
155 if (buttons & BM_S1)
156 stream.print(" 2");
157 if (buttons & BM_S2)
158 stream.print(" 3");
159 if (buttons & BM_S3)
160 stream.print(" 4");
161 stream.print(", lowbat=");
162 stream.print(lowbat, DEC);
163 stream.print(", code=");
164 stream.print(encrypted, HEX);
165 stream.print("\n");
166}
167
168#define PULSE_WIDTH 400
169
170inline void HCS200_Keycode::send(bool value, std::function<void(int)> setOutput)
171{
172 if (!value)
173 {
174 setOutput(HIGH);
175 delayMicroseconds(PULSE_WIDTH);
176 delayMicroseconds(PULSE_WIDTH);
177 setOutput(LOW);
178 delayMicroseconds(PULSE_WIDTH);
179 }
180 else
181 {
182 setOutput(HIGH);
183 delayMicroseconds(PULSE_WIDTH);
184 setOutput(LOW);
185 delayMicroseconds(PULSE_WIDTH);
186 delayMicroseconds(PULSE_WIDTH);
187 }
188}
189
190void HCS200_Keycode::send(std::function<void(int)> setOutput)
191{
192 uint32_t val;
193
194 // preamble
195 for(unsigned short i = 0; i < 11; i++)
196 {
197 setOutput(HIGH);
198 delayMicroseconds(PULSE_WIDTH);
199 setOutput(LOW);
200 delayMicroseconds(PULSE_WIDTH);
201 }
202 setOutput(HIGH);
203 delayMicroseconds(PULSE_WIDTH);
204
205 // header
206 setOutput(LOW);
207 delayMicroseconds(PULSE_WIDTH * 10);
208
209 // encrypted
210 val = this->encrypted;
211 for(unsigned short i = 0; i < 32; i++)
212 {
213 send(val & 0x01, setOutput);
214 val >>= 1;
215 }
216
217 // serial
218 val = this->serial;
219 for(unsigned short i = 0; i < 28; i++)
220 {
221 send(val & 0x01, setOutput);
222 val >>= 1;
223 }
224
225 // buttons
226 val = this->buttons;
227 for(unsigned short i = 0; i < 4; i++)
228 {
229 send(val & 0x01, setOutput);
230 val >>= 1;
231 }
232
233 // lowbat
234 send(this->lowbat, setOutput);
235
236 // RPT
237 send(1, setOutput);
238
239 // guard time
240 setOutput(LOW);
241 delayMicroseconds(PULSE_WIDTH * 39);
242}