-
Notifications
You must be signed in to change notification settings - Fork 274
/
Copy pathNewRemoteReceiver.cpp
318 lines (275 loc) · 9.82 KB
/
NewRemoteReceiver.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/*
* NewRemoteSwitch library v1.1.0 (20130601) made by Randy Simons http://randysimons.nl/
* See NewRemoteReceiver.h for details.
*
* License: GPLv3. See license.txt
*/
#include "NewRemoteReceiver.h"
/************
* NewRemoteReceiver
Protocol. (Copied from Wieltje, http://www.circuitsonline.net/forum/view/message/1181410#1181410,
but with slightly different timings, as measured on my device.)
_ _
'0': | |_| |_____ (T,T,T,5T)
_ _
'1': | |_____| |_ (T,5T,T,T)
_ _
dim: | |_| |_ (T,T,T,T)
T = short period of ~260µs. However, this code tries
to figure out the correct period
A full frame looks like this:
- start pulse: 1T high, 10.44T low
- 26 bit: Address
- 1 bit: group bit
- 1 bit: on/off/[dim]
- 4 bit: unit
- [4 bit: dim level. Only present of [dim] is chosen]
- stop pulse: 1T high, 40T low
************/
int8_t NewRemoteReceiver::_interrupt;
volatile short NewRemoteReceiver::_state;
byte NewRemoteReceiver::_minRepeats;
NewRemoteReceiverCallBack NewRemoteReceiver::_callback;
boolean NewRemoteReceiver::_inCallback = false;
boolean NewRemoteReceiver::_enabled = false;
void NewRemoteReceiver::init(int8_t interrupt, byte minRepeats, NewRemoteReceiverCallBack callback) {
_interrupt = interrupt;
_minRepeats = minRepeats;
_callback = callback;
enable();
if (_interrupt >= 0) {
attachInterrupt(_interrupt, interruptHandler, CHANGE);
}
}
void NewRemoteReceiver::enable() {
_state = -1;
_enabled = true;
}
void NewRemoteReceiver::disable() {
_enabled = false;
}
void NewRemoteReceiver::deinit() {
_enabled = false;
if (_interrupt >= 0) {
detachInterrupt(_interrupt);
}
}
void NewRemoteReceiver::interruptHandler() {
// This method is written as compact code to keep it fast. While breaking up this method into more
// methods would certainly increase the readability, it would also be much slower to execute.
// Making calls to other methods is quite expensive on AVR. As These interrupt handlers are called
// many times a second, calling other methods should be kept to a minimum.
if (!_enabled) {
return;
}
static byte receivedBit; // Contains "bit" currently receiving
static NewRemoteCode receivedCode; // Contains received code
static NewRemoteCode previousCode; // Contains previous received code
static byte repeats = 0; // The number of times the an identical code is received in a row.
static unsigned long edgeTimeStamp[3] = {0, }; // Timestamp of edges
static unsigned int min1Period, max1Period, min5Period, max5Period;
static bool skip;
// Filter out too short pulses. This method works as a low pass filter.
edgeTimeStamp[1] = edgeTimeStamp[2];
edgeTimeStamp[2] = micros();
if (skip) {
skip = false;
return;
}
if (_state >= 0 && edgeTimeStamp[2]-edgeTimeStamp[1] < min1Period) {
// Last edge was too short.
// Skip this edge, and the next too.
skip = true;
return;
}
unsigned int duration = edgeTimeStamp[1] - edgeTimeStamp[0];
edgeTimeStamp[0] = edgeTimeStamp[1];
// Note that if state>=0, duration is always >= 1 period.
if (_state == -1) {
// wait for the long low part of a stop bit.
// Stopbit: 1T high, 40T low
// By default 1T is 260µs, but for maximum compatibility go as low as 120µs
if (duration > 4800) { // =40*120µs, minimal time between two edges before decoding starts.
// Sync signal received.. Preparing for decoding
repeats = 0;
receivedCode.period = duration / 40; // Measured signal is 40T, so 1T (period) is measured signal / 40.
// Allow for large error-margin. ElCheapo-hardware :(
min1Period = receivedCode.period * 3 / 10; // Lower limit for 1 period is 0.3 times measured period; high signals can "linger" a bit sometimes, making low signals quite short.
max1Period = receivedCode.period * 3; // Upper limit for 1 period is 3 times measured period
min5Period = receivedCode.period * 3; // Lower limit for 5 periods is 3 times measured period
max5Period = receivedCode.period * 8; // Upper limit for 5 periods is 8 times measured period
}
else {
return;
}
} else if (_state == 0) { // Verify start bit part 1 of 2
// Duration must be ~1T
if (duration > max1Period) {
_state = -1;
return;
}
// Start-bit passed. Do some clean-up.
receivedCode.address = receivedCode.unit = receivedCode.dimLevel = 0;
} else if (_state == 1) { // Verify start bit part 2 of 2
// Duration must be ~10.44T
if (duration < 7 * receivedCode.period || duration > 15 * receivedCode.period) {
_state = -1;
return;
}
} else if (_state < 148) { // state 146 is first edge of stop-sequence. All bits before that adhere to default protocol, with exception of dim-bit
receivedBit <<= 1;
// One bit consists out of 4 bit parts.
// bit part durations can ONLY be 1 or 5 periods.
if (duration <= max1Period) {
receivedBit &= B1110; // Clear LSB of receivedBit
} else if (duration >= min5Period && duration <= max5Period) {
receivedBit |= B1; // Set LSB of receivedBit
} else if (
// Check if duration matches the second part of stopbit (duration must be ~40T), and ...
(duration >= 20 * receivedCode.period && duration <= 80 * receivedCode.period) &&
// if first part op stopbit was a short signal (short signal yielded a 0 as second bit in receivedBit), and ...
((receivedBit & B10) == B00) &&
// we are in a state in which a stopbit is actually valid, only then ...
(_state == 147 || _state == 131) ) {
// If a dim-level was present...
if (_state == 147) {
// ... test if it was an "on" signal ...
if (receivedCode.switchType == NewRemoteCode::on) {
// ... set the appropriate switch type
receivedCode.switchType = NewRemoteCode::on_with_dim;
} else {
// ... otherwise it was wrong (e.g. off-signal with dim)
_state = -1;
return;
}
}
// a valid signal was found!
if (
receivedCode.address != previousCode.address ||
receivedCode.unit != previousCode.unit ||
receivedCode.dimLevel != previousCode.dimLevel ||
receivedCode.groupBit != previousCode.groupBit ||
receivedCode.switchType != previousCode.switchType
) { // memcmp isn't deemed safe
repeats=0;
previousCode = receivedCode;
}
repeats++;
if (repeats>=_minRepeats) {
if (!_inCallback) {
_inCallback = true;
(_callback)(receivedCode);
_inCallback = false;
}
// Reset after callback.
_state=-1;
return;
}
// Reset for next round
_state=0; // no need to wait for another sync-bit!
return;
}
else { // Otherwise the entire sequence is invalid
_state = -1;
return;
}
if (_state % 4 == 1) { // Last bit part? Note: this is the short version of "if ( (_state-2) % 4 == 3 )"
// There are 3 valid options for receivedBit:
// 0, indicated by short short short long == B0001.
// 1, short long shot short == B0100.
// dim, short shot short shot == B0000.
// Everything else: inconsistent data, trash the whole sequence.
if (_state < 106) {
// States 2 - 105 are address bit states
receivedCode.address <<= 1;
// Decode bit. Only 4 LSB's of receivedBit are used; trim the rest.
switch (receivedBit & B1111) {
case B0001: // Bit "0" received.
// receivedCode.address |= 0; But let's not do that, as it is wasteful.
break;
case B0100: // Bit "1" received.
receivedCode.address |= 1;
break;
default: // Bit was invalid. Abort.
_state = -1;
return;
}
} else if (_state < 110) {
// States 106 - 109 are group bit states.
switch (receivedBit & B1111) {
case B0001: // Bit "0" received.
receivedCode.groupBit = false;
break;
case B0100: // Bit "1" received.
receivedCode.groupBit = true;
break;
default: // Bit was invalid. Abort.
_state = -1;
return;
}
} else if (_state < 114) {
// States 110 - 113 are switch bit states.
switch (receivedBit & B1111) {
case B0001: // Bit "0" received.
receivedCode.switchType = NewRemoteCode::off;
break;
case B0100: // Bit "1" received. Note: this might turn out to be a on_with_dim signal.
receivedCode.switchType = NewRemoteCode::on;
break;
case B0000: // Bit "dim" received.
receivedCode.switchType = NewRemoteCode::dim;
break;
default: // Bit was invalid. Abort.
_state = -1;
return;
}
} else if (_state < 130){
// States 114 - 129 are unit bit states.
receivedCode.unit <<= 1;
// Decode bit.
switch (receivedBit & B1111) {
case B0001: // Bit "0" received.
// receivedCode.unit |= 0; But let's not do that, as it is wasteful.
break;
case B0100: // Bit "1" received.
receivedCode.unit |= 1;
break;
default: // Bit was invalid. Abort.
_state = -1;
return;
}
} else if (_state < 146) {
// States 130 - 145 are dim bit states.
// If switchType == 0 these are never present.
// If switchType == 2 these are always present.
// If switchType == 1 these are or are not present, depending on the revision of the transmitter.
receivedCode.dimLevel <<= 1;
// Decode bit.
switch (receivedBit & B1111) {
case B0001: // Bit "0" received.
// receivedCode.dimLevel |= 0; But let's not do that, as it is wasteful.
break;
case B0100: // Bit "1" received.
receivedCode.dimLevel |= 1;
break;
default: // Bit was invalid. Abort.
_state = -1;
return;
}
}
}
}
_state++;
return;
}
boolean NewRemoteReceiver::isReceiving(int waitMillis) {
unsigned long startTime=millis();
int waited; // Signed int!
do {
if (_state >= 34) { // Abort if a significant part of a code (start pulse + 8 bits) has been received
return true;
}
waited = (millis()-startTime);
} while(waited>=0 && waited <= waitMillis); // Yes, clock wraps every 50 days. And then you'd have to wait for a looooong time.
return false;
}