ESPHome 2026.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 (robert@tideys.net)
6
7#ifdef USE_ESP8266
8
9#include "LwRx.h"
10#include <cstring>
12
13namespace esphome::lightwaverf {
14
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 // Copy message under interrupt lock to prevent ISR overwriting rx_msg mid-read
189 uint8_t msg_copy[RX_MSGLEN];
190 {
191 InterruptLock lock;
192 memcpy(msg_copy, this->rx_msg, RX_MSGLEN);
193 this->rx_msgcomplete = false;
194 }
195 for (uint8_t i = 0; ret && i < RX_MSGLEN; i++) {
196 if (this->rx_translate || (len != RX_MSGLEN)) {
197 j = this->rx_find_nibble_(msg_copy[i]);
198 if (j < 0)
199 ret = false;
200 } else {
201 j = msg_copy[i];
202 }
203 switch (len) {
204 case 4:
205 if (i == 9)
206 buf[2] = j;
207 if (i == 2)
208 buf[3] = j;
209 [[fallthrough]];
210 case 2:
211 if (i == 3)
212 buf[0] = j;
213 if (i == 0)
214 buf[1] = j << 4;
215 if (i == 1)
216 buf[1] += j;
217 break;
218 case 10:
219 buf[i] = j;
220 break;
221 }
222 }
223 } else {
224 ret = false;
225 }
226 return ret;
227}
228
233
237void LwRx::lwrx_setfilter(uint8_t repeats, uint8_t timeout) {
238 this->rx_repeats = repeats;
239 this->rx_timeout = timeout;
240}
241
247uint8_t LwRx::lwrx_addpair(const uint8_t *pairdata) {
248 if (this->rx_paircount < RX_MAXPAIRS) {
249 for (uint8_t i = 0; i < 8; i++) {
250 this->rx_pairs[rx_paircount][i] = RX_NIBBLE[pairdata[i]];
251 }
252 this->rx_paircommit_();
253 }
254 return this->rx_paircount;
255}
256
260void LwRx::lwrx_makepair(uint8_t timeout) {
261 this->rx_pairtimeout = timeout;
262 this->rx_pairstarttime = millis();
263}
264
268uint8_t LwRx::lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber) {
269 if (pairnumber < this->rx_paircount) {
270 int16_t j; // int
271 for (uint8_t i = 0; i < 8; i++) {
272 j = this->rx_find_nibble_(this->rx_pairs[pairnumber][i]);
273 if (j >= 0)
274 pairdata[i] = j;
275 }
276 }
277 return this->rx_paircount;
278}
279
284
288bool LwRx::lwrx_getstats_(uint16_t *stats) { // unsigned int
289 if (this->lwrx_stats_enable) {
290 memcpy(stats, this->lwrx_stats, 2 * RX_STAT_COUNT);
291 return true;
292 } else {
293 return false;
294 }
295}
296
300void LwRx::lwrx_setstatsenable_(bool rx_stats_enable) {
301 this->lwrx_stats_enable = rx_stats_enable;
302 if (!this->lwrx_stats_enable) {
303 // clear down stats when disabling
304 memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
305 }
306}
310void LwRx::lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only) {
311 this->rx_pairEnforce = pair_enforce;
312 this->rx_pairBaseOnly = pair_base_only;
313}
314
321 // rx_pin = pin;
322 pin->setup();
323 this->rx_pin_isr_ = pin->to_isr();
325
326 memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
327}
328
334 if (this->rx_pairEnforce && this->rx_paircount == 0) {
335 return false;
336 } else {
337 bool all_devices;
338 // True if mood to device 15 or Off cmd with Allof paramater
339 all_devices = ((this->rx_msg[3] == RX_CMD_MOOD && this->rx_msg[2] == RX_DEV_15) ||
340 (this->rx_msg[3] == RX_CMD_OFF && this->rx_msg[0] == RX_PAR0_ALLOFF));
341 return (rx_check_pairs_(&this->rx_msg[2], all_devices) != -1);
342 }
343}
348int16_t LwRx::rx_find_nibble_(uint8_t data) { // int
349 int16_t i = 15; // int
350 do {
351 if (RX_NIBBLE[i] == data)
352 break;
353 i--;
354 } while (i >= 0);
355 return i;
356}
357
362 if (this->rx_paircount < RX_MAXPAIRS) {
363 memcpy(this->rx_pairs[this->rx_paircount], &this->rx_msg[2], 8);
364 this->rx_paircommit_();
365 }
366}
367
372 if (this->rx_paircount == 0 || this->rx_check_pairs_(this->rx_pairs[this->rx_paircount], false) < 0) {
373 this->rx_paircount++;
374 }
375}
376
383int16_t LwRx::rx_check_pairs_(const uint8_t *buf, bool all_devices) { // int
384 if (this->rx_paircount == 0) {
385 return -2;
386 } else {
387 int16_t pair = this->rx_paircount; // int
388 int16_t j = -1; // int
389 int16_t jstart, jend; // int
390 if (this->rx_pairBaseOnly) {
391 // skip room(8) and dev/cmd (0,1)
392 jstart = 7;
393 jend = 2;
394 } else {
395 // include room in comparison
396 jstart = 8;
397 // skip device comparison if allDevices true
398 jend = (all_devices) ? 2 : 0;
399 }
400 while (pair > 0 && j < 0) {
401 pair--;
402 j = jstart;
403 while (j > jend) {
404 j--;
405 if (j != 1) {
406 if (this->rx_pairs[pair][j] != buf[j]) {
407 j = -1;
408 }
409 }
410 }
411 }
412 return (j >= 0) ? pair : -1;
413 }
414}
415
419void LwRx::rx_remove_pair_(uint8_t *buf) {
420 int16_t pair = this->rx_check_pairs_(buf, false); // int
421 if (pair >= 0) {
422 while (pair < this->rx_paircount - 1) {
423 for (uint8_t j = 0; j < 8; j++) {
424 this->rx_pairs[pair][j] = this->rx_pairs[pair + 1][j];
425 }
426 pair++;
427 }
428 this->rx_paircount--;
429 }
430}
431
432} // namespace esphome::lightwaverf
433#endif
virtual void setup()=0
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:107
virtual ISRInternalGPIOPin to_isr() const =0
Helper class to disable interrupts.
Definition helpers.h:1952
int16_t rx_find_nibble_(uint8_t data)
Find nibble from byte returns -1 if none found.
Definition LwRx.cpp:348
volatile bool rx_msgcomplete
Definition LwRx.h:107
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:333
void rx_paircommit_()
check and commit pair
Definition LwRx.cpp:371
uint16_t lwrx_stats[RX_STAT_COUNT]
Definition LwRx.h:115
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:383
bool lwrx_getstats_(uint16_t *stats)
Return stats on high and low pulses.
Definition LwRx.cpp:288
uint32_t lwrx_packetinterval()
Return time in milliseconds since last packet received.
Definition LwRx.cpp:232
ISRInternalGPIOPin rx_pin_isr_
Definition LwRx.h:136
void lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only)
Set pairs behaviour.
Definition LwRx.cpp:310
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:320
void rx_addpairfrommsg_()
add pair from message buffer
Definition LwRx.cpp:361
uint8_t lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber)
Get pair data (translated back to nibble form.
Definition LwRx.cpp:268
uint32_t rx_pairstarttime
Definition LwRx.h:99
uint8_t rx_msg[RX_MSGLEN]
Definition LwRx.h:102
void lwrx_setstatsenable_(bool rx_stats_enable)
Set stats mode.
Definition LwRx.cpp:300
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:247
bool lwrx_message()
Test if a message has arrived.
Definition LwRx.cpp:175
uint32_t rx_prevpkttime
Definition LwRx.h:98
void rx_remove_pair_(uint8_t *buf)
Remove an existing pair matching the buffer.
Definition LwRx.cpp:419
uint8_t rx_pairs[RX_MAXPAIRS][8]
Definition LwRx.h:86
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:237
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:260
void lwrx_clearpairing_()
Clear all pairing.
Definition LwRx.cpp:283
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:52
const char int const __FlashStringHelper va_list args
Definition log.h:74
std::string size_t len
uint32_t IRAM_ATTR HOT micros()
Definition hal.cpp:43
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:28
static void uint32_t