ESPHome 2025.5.0
Loading...
Searching...
No Matches
LwRx.cpp
Go to the documentation of this file.
1// LwRx.cpp
2//
3// LightwaveRF 434MHz receiver interface for Arduino
4//
5// Author: Bob Tidey ([email protected])
6
7#ifdef USE_ESP8266
8
9#include "LwRx.h"
10#include <cstring>
11
12namespace esphome {
13namespace lightwaverf {
14
20void IRAM_ATTR LwRx::rx_process_bits(LwRx *args) {
21 uint8_t event = args->rx_pin_isr_.digital_read(); // start setting event to the current value
22 uint32_t curr = micros(); // the current time in microseconds
23
24 uint16_t dur = (curr - args->rx_prev); // unsigned int
25 args->rx_prev = curr;
26 // set event based on input and duration of previous pulse
27 if (dur < 120) { // 120 very short
28 } else if (dur < 500) { // normal short pulse
29 event += 2;
30 } else if (dur < 2000) { // normal long pulse
31 event += 4;
32 } else if (dur > 5000) { // gap between messages
33 event += 6;
34 } else { // 2000 > 5000
35 event = 8; // illegal gap
36 }
37 // state machine transitions
38 switch (args->rx_state) {
39 case RX_STATE_IDLE:
40 switch (event) {
41 case 7: // 1 after a message gap
42 args->rx_state = RX_STATE_MSGSTARTFOUND;
43 break;
44 }
45 break;
46 case RX_STATE_MSGSTARTFOUND:
47 switch (event) {
48 case 2: // 0 160->500
49 // nothing to do wait for next positive edge
50 break;
51 case 3: // 1 160->500
52 args->rx_num_bytes = 0;
53 args->rx_state = RX_STATE_BYTESTARTFOUND;
54 break;
55 default:
56 // not good start again
57 args->rx_state = RX_STATE_IDLE;
58 break;
59 }
60 break;
61 case RX_STATE_BYTESTARTFOUND:
62 switch (event) {
63 case 2: // 0 160->500
64 // nothing to do wait for next positive edge
65 break;
66 case 3: // 1 160->500
67 args->rx_state = RX_STATE_GETBYTE;
68 args->rx_num_bits = 0;
69 break;
70 case 5: // 0 500->1500
71 args->rx_state = RX_STATE_GETBYTE;
72 // Starts with 0 so put this into uint8_t
73 args->rx_num_bits = 1;
74 args->rx_buf[args->rx_num_bytes] = 0;
75 break;
76 default:
77 // not good start again
78 args->rx_state = RX_STATE_IDLE;
79 break;
80 }
81 break;
82 case RX_STATE_GETBYTE:
83 switch (event) {
84 case 2: // 0 160->500
85 // nothing to do wait for next positive edge but do stats
86 if (args->lwrx_stats_enable) {
87 args->lwrx_stats[RX_STAT_HIGH_MAX] = std::max(args->lwrx_stats[RX_STAT_HIGH_MAX], dur);
88 args->lwrx_stats[RX_STAT_HIGH_MIN] = std::min(args->lwrx_stats[RX_STAT_HIGH_MIN], dur);
89 args->lwrx_stats[RX_STAT_HIGH_AVE] =
90 args->lwrx_stats[RX_STAT_HIGH_AVE] - (args->lwrx_stats[RX_STAT_HIGH_AVE] >> 4) + dur;
91 }
92 break;
93 case 3: // 1 160->500
94 // a single 1
95 args->rx_buf[args->rx_num_bytes] = args->rx_buf[args->rx_num_bytes] << 1 | 1;
96 args->rx_num_bits++;
97 if (args->lwrx_stats_enable) {
98 args->lwrx_stats[RX_STAT_LOW1_MAX] = std::max(args->lwrx_stats[RX_STAT_LOW1_MAX], dur);
99 args->lwrx_stats[RX_STAT_LOW1_MIN] = std::min(args->lwrx_stats[RX_STAT_LOW1_MIN], dur);
100 args->lwrx_stats[RX_STAT_LOW1_AVE] =
101 args->lwrx_stats[RX_STAT_LOW1_AVE] - (args->lwrx_stats[RX_STAT_LOW1_AVE] >> 4) + dur;
102 }
103 break;
104 case 5: // 1 500->1500
105 // a 1 followed by a 0
106 args->rx_buf[args->rx_num_bytes] = args->rx_buf[args->rx_num_bytes] << 2 | 2;
107 args->rx_num_bits++;
108 args->rx_num_bits++;
109 if (args->lwrx_stats_enable) {
110 args->lwrx_stats[RX_STAT_LOW0_MAX] = std::max(args->lwrx_stats[RX_STAT_LOW0_MAX], dur);
111 args->lwrx_stats[RX_STAT_LOW0_MIN] = std::min(args->lwrx_stats[RX_STAT_LOW0_MIN], dur);
112 args->lwrx_stats[RX_STAT_LOW0_AVE] =
113 args->lwrx_stats[RX_STAT_LOW0_AVE] - (args->lwrx_stats[RX_STAT_LOW0_AVE] >> 4) + dur;
114 }
115 break;
116 default:
117 // not good start again
118 args->rx_state = RX_STATE_IDLE;
119 break;
120 }
121 if (args->rx_num_bits >= 8) {
122 args->rx_num_bytes++;
123 args->rx_num_bits = 0;
124 if (args->rx_num_bytes >= RX_MSGLEN) {
125 uint32_t curr_millis = millis();
126 if (args->rx_repeats > 0) {
127 if ((curr_millis - args->rx_prevpkttime) / 100 > args->rx_timeout) {
128 args->rx_repeatcount = 1;
129 } else {
130 // Test message same as last one
131 int16_t i = RX_MSGLEN; // int
132 do {
133 i--;
134 } while ((i >= 0) && (args->rx_msg[i] == args->rx_buf[i]));
135 if (i < 0) {
136 args->rx_repeatcount++;
137 } else {
138 args->rx_repeatcount = 1;
139 }
140 }
141 } else {
142 args->rx_repeatcount = 0;
143 }
144 args->rx_prevpkttime = curr_millis;
145 // If last message hasn't been read it gets overwritten
146 memcpy(args->rx_msg, args->rx_buf, RX_MSGLEN);
147 if (args->rx_repeats == 0 || args->rx_repeatcount == args->rx_repeats) {
148 if (args->rx_pairtimeout != 0) {
149 if ((curr_millis - args->rx_pairstarttime) / 100 <= args->rx_pairtimeout) {
150 if (args->rx_msg[3] == RX_CMD_ON) {
151 args->rx_addpairfrommsg_();
152 } else if (args->rx_msg[3] == RX_CMD_OFF) {
153 args->rx_remove_pair_(&args->rx_msg[2]);
154 }
155 }
156 }
157 if (args->rx_report_message_()) {
158 args->rx_msgcomplete = true;
159 }
160 args->rx_pairtimeout = 0;
161 }
162 // And cycle round for next one
163 args->rx_state = RX_STATE_IDLE;
164 } else {
165 args->rx_state = RX_STATE_BYTESTARTFOUND;
166 }
167 }
168 break;
169 }
170}
171
175bool LwRx::lwrx_message() { return (this->rx_msgcomplete); }
176
180void LwRx::lwrx_settranslate(bool rxtranslate) { this->rx_translate = rxtranslate; }
184bool LwRx::lwrx_getmessage(uint8_t *buf, uint8_t len) {
185 bool ret = true;
186 int16_t j = 0; // int
187 if (this->rx_msgcomplete && len <= RX_MSGLEN) {
188 for (uint8_t i = 0; ret && i < RX_MSGLEN; i++) {
189 if (this->rx_translate || (len != RX_MSGLEN)) {
190 j = this->rx_find_nibble_(this->rx_msg[i]);
191 if (j < 0)
192 ret = false;
193 } else {
194 j = this->rx_msg[i];
195 }
196 switch (len) {
197 case 4:
198 if (i == 9)
199 buf[2] = j;
200 if (i == 2)
201 buf[3] = j;
202 case 2:
203 if (i == 3)
204 buf[0] = j;
205 if (i == 0)
206 buf[1] = j << 4;
207 if (i == 1)
208 buf[1] += j;
209 break;
210 case 10:
211 buf[i] = j;
212 break;
213 }
214 }
215 this->rx_msgcomplete = false;
216 } else {
217 ret = false;
218 }
219 return ret;
220}
221
225uint32_t LwRx::lwrx_packetinterval() { return millis() - this->rx_prevpkttime; }
226
230void LwRx::lwrx_setfilter(uint8_t repeats, uint8_t timeout) {
231 this->rx_repeats = repeats;
232 this->rx_timeout = timeout;
233}
234
240uint8_t LwRx::lwrx_addpair(const uint8_t *pairdata) {
241 if (this->rx_paircount < RX_MAXPAIRS) {
242 for (uint8_t i = 0; i < 8; i++) {
243 this->rx_pairs[rx_paircount][i] = RX_NIBBLE[pairdata[i]];
244 }
245 this->rx_paircommit_();
246 }
247 return this->rx_paircount;
248}
249
253void LwRx::lwrx_makepair(uint8_t timeout) {
254 this->rx_pairtimeout = timeout;
255 this->rx_pairstarttime = millis();
256}
257
261uint8_t LwRx::lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber) {
262 if (pairnumber < this->rx_paircount) {
263 int16_t j; // int
264 for (uint8_t i = 0; i < 8; i++) {
265 j = this->rx_find_nibble_(this->rx_pairs[pairnumber][i]);
266 if (j >= 0)
267 pairdata[i] = j;
268 }
269 }
270 return this->rx_paircount;
271}
272
277
281bool LwRx::lwrx_getstats_(uint16_t *stats) { // unsigned int
282 if (this->lwrx_stats_enable) {
283 memcpy(stats, this->lwrx_stats, 2 * RX_STAT_COUNT);
284 return true;
285 } else {
286 return false;
287 }
288}
289
293void LwRx::lwrx_setstatsenable_(bool rx_stats_enable) {
294 this->lwrx_stats_enable = rx_stats_enable;
295 if (!this->lwrx_stats_enable) {
296 // clear down stats when disabling
297 memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
298 }
299}
303void LwRx::lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only) {
304 this->rx_pairEnforce = pair_enforce;
305 this->rx_pairBaseOnly = pair_base_only;
306}
307
314 // rx_pin = pin;
315 pin->setup();
316 this->rx_pin_isr_ = pin->to_isr();
318
319 memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
320}
321
327 if (this->rx_pairEnforce && this->rx_paircount == 0) {
328 return false;
329 } else {
330 bool all_devices;
331 // True if mood to device 15 or Off cmd with Allof paramater
332 all_devices = ((this->rx_msg[3] == RX_CMD_MOOD && this->rx_msg[2] == RX_DEV_15) ||
333 (this->rx_msg[3] == RX_CMD_OFF && this->rx_msg[0] == RX_PAR0_ALLOFF));
334 return (rx_check_pairs_(&this->rx_msg[2], all_devices) != -1);
335 }
336}
341int16_t LwRx::rx_find_nibble_(uint8_t data) { // int
342 int16_t i = 15; // int
343 do {
344 if (RX_NIBBLE[i] == data)
345 break;
346 i--;
347 } while (i >= 0);
348 return i;
349}
350
355 if (this->rx_paircount < RX_MAXPAIRS) {
356 memcpy(this->rx_pairs[this->rx_paircount], &this->rx_msg[2], 8);
357 this->rx_paircommit_();
358 }
359}
360
365 if (this->rx_paircount == 0 || this->rx_check_pairs_(this->rx_pairs[this->rx_paircount], false) < 0) {
366 this->rx_paircount++;
367 }
368}
369
376int16_t LwRx::rx_check_pairs_(const uint8_t *buf, bool all_devices) { // int
377 if (this->rx_paircount == 0) {
378 return -2;
379 } else {
380 int16_t pair = this->rx_paircount; // int
381 int16_t j = -1; // int
382 int16_t jstart, jend; // int
383 if (this->rx_pairBaseOnly) {
384 // skip room(8) and dev/cmd (0,1)
385 jstart = 7;
386 jend = 2;
387 } else {
388 // include room in comparison
389 jstart = 8;
390 // skip device comparison if allDevices true
391 jend = (all_devices) ? 2 : 0;
392 }
393 while (pair > 0 && j < 0) {
394 pair--;
395 j = jstart;
396 while (j > jend) {
397 j--;
398 if (j != 1) {
399 if (this->rx_pairs[pair][j] != buf[j]) {
400 j = -1;
401 }
402 }
403 }
404 }
405 return (j >= 0) ? pair : -1;
406 }
407}
408
412void LwRx::rx_remove_pair_(uint8_t *buf) {
413 int16_t pair = this->rx_check_pairs_(buf, false); // int
414 if (pair >= 0) {
415 while (pair < this->rx_paircount - 1) {
416 for (uint8_t j = 0; j < 8; j++) {
417 this->rx_pairs[pair][j] = this->rx_pairs[pair + 1][j];
418 }
419 pair++;
420 }
421 this->rx_paircount--;
422 }
423}
424
425} // namespace lightwaverf
426} // namespace esphome
427#endif
virtual void setup()=0
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:88
virtual ISRInternalGPIOPin to_isr() const =0
int16_t rx_find_nibble_(uint8_t data)
Find nibble from byte returns -1 if none found.
Definition LwRx.cpp:341
uint8_t rx_buf[RX_MSGLEN]
Definition LwRx.h:104
bool rx_report_message_()
Check a message to see if it should be reported under pairing / mood / all off rules returns -1 if no...
Definition LwRx.cpp:326
void rx_paircommit_()
check and commit pair
Definition LwRx.cpp:364
uint16_t lwrx_stats[RX_STAT_COUNT]
Definition LwRx.h:116
int16_t rx_check_pairs_(const uint8_t *buf, bool all_devices)
Check to see if message matches one of the pairs if mode is pairBase only then ignore device and room...
Definition LwRx.cpp:376
bool lwrx_getstats_(uint16_t *stats)
Return stats on high and low pulses.
Definition LwRx.cpp:281
uint32_t lwrx_packetinterval()
Return time in milliseconds since last packet received.
Definition LwRx.cpp:225
ISRInternalGPIOPin rx_pin_isr_
Definition LwRx.h:137
void lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only)
Set pairs behaviour.
Definition LwRx.cpp:303
void lwrx_setup(InternalGPIOPin *pin)
Set things up to receive LightWaveRF 434Mhz messages pin must be 2 or 3 to trigger interrupts !...
Definition LwRx.cpp:313
void rx_addpairfrommsg_()
add pair from message buffer
Definition LwRx.cpp:354
uint8_t lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber)
Get pair data (translated back to nibble form.
Definition LwRx.cpp:261
uint32_t rx_pairstarttime
Definition LwRx.h:100
uint8_t rx_msg[RX_MSGLEN]
Definition LwRx.h:103
void lwrx_setstatsenable_(bool rx_stats_enable)
Set stats mode.
Definition LwRx.cpp:293
uint8_t lwrx_addpair(const uint8_t *pairdata)
Add a pair to filter received messages pairdata is device,dummy,5*addr,room pairdata is held in trans...
Definition LwRx.cpp:240
bool lwrx_message()
Test if a message has arrived.
Definition LwRx.cpp:175
uint32_t rx_prevpkttime
Definition LwRx.h:99
void rx_remove_pair_(uint8_t *buf)
Remove an existing pair matching the buffer.
Definition LwRx.cpp:412
uint8_t rx_pairs[RX_MAXPAIRS][8]
Definition LwRx.h:87
static void rx_process_bits(LwRx *arg)
Pin change interrupt routine that identifies 1 and 0 LightwaveRF bits and constructs a message when a...
Definition LwRx.cpp:20
void lwrx_setfilter(uint8_t repeats, uint8_t timeout)
Set up repeat filtering of received messages.
Definition LwRx.cpp:230
void lwrx_settranslate(bool translate)
Set translate mode.
Definition LwRx.cpp:180
void lwrx_makepair(uint8_t timeout)
Make a pair from next message successfully received.
Definition LwRx.cpp:253
void lwrx_clearpairing_()
Clear all pairing.
Definition LwRx.cpp:276
bool lwrx_getmessage(uint8_t *buf, uint8_t len)
Transfer a message to user buffer.
Definition LwRx.cpp:184
@ INTERRUPT_ANY_EDGE
Definition gpio.h:43
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:301
uint32_t IRAM_ATTR HOT micros()
Definition core.cpp:29
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27