ESPHome 2026.3.3
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 {
14namespace lightwaverf {
15
22 uint8_t event = args->rx_pin_isr_.digital_read(); // start setting event to the current value
23 uint32_t curr = micros(); // the current time in microseconds
24
25 uint16_t dur = (curr - args->rx_prev); // unsigned int
26 args->rx_prev = curr;
27 // set event based on input and duration of previous pulse
28 if (dur < 120) { // 120 very short
29 } else if (dur < 500) { // normal short pulse
30 event += 2;
31 } else if (dur < 2000) { // normal long pulse
32 event += 4;
33 } else if (dur > 5000) { // gap between messages
34 event += 6;
35 } else { // 2000 > 5000
36 event = 8; // illegal gap
37 }
38 // state machine transitions
39 switch (args->rx_state) {
40 case RX_STATE_IDLE:
41 switch (event) {
42 case 7: // 1 after a message gap
43 args->rx_state = RX_STATE_MSGSTARTFOUND;
44 break;
45 }
46 break;
47 case RX_STATE_MSGSTARTFOUND:
48 switch (event) {
49 case 2: // 0 160->500
50 // nothing to do wait for next positive edge
51 break;
52 case 3: // 1 160->500
53 args->rx_num_bytes = 0;
54 args->rx_state = RX_STATE_BYTESTARTFOUND;
55 break;
56 default:
57 // not good start again
58 args->rx_state = RX_STATE_IDLE;
59 break;
60 }
61 break;
62 case RX_STATE_BYTESTARTFOUND:
63 switch (event) {
64 case 2: // 0 160->500
65 // nothing to do wait for next positive edge
66 break;
67 case 3: // 1 160->500
68 args->rx_state = RX_STATE_GETBYTE;
69 args->rx_num_bits = 0;
70 break;
71 case 5: // 0 500->1500
72 args->rx_state = RX_STATE_GETBYTE;
73 // Starts with 0 so put this into uint8_t
74 args->rx_num_bits = 1;
75 args->rx_buf[args->rx_num_bytes] = 0;
76 break;
77 default:
78 // not good start again
79 args->rx_state = RX_STATE_IDLE;
80 break;
81 }
82 break;
83 case RX_STATE_GETBYTE:
84 switch (event) {
85 case 2: // 0 160->500
86 // nothing to do wait for next positive edge but do stats
87 if (args->lwrx_stats_enable) {
88 args->lwrx_stats[RX_STAT_HIGH_MAX] = std::max(args->lwrx_stats[RX_STAT_HIGH_MAX], dur);
89 args->lwrx_stats[RX_STAT_HIGH_MIN] = std::min(args->lwrx_stats[RX_STAT_HIGH_MIN], dur);
90 args->lwrx_stats[RX_STAT_HIGH_AVE] =
91 args->lwrx_stats[RX_STAT_HIGH_AVE] - (args->lwrx_stats[RX_STAT_HIGH_AVE] >> 4) + dur;
92 }
93 break;
94 case 3: // 1 160->500
95 // a single 1
96 args->rx_buf[args->rx_num_bytes] = args->rx_buf[args->rx_num_bytes] << 1 | 1;
97 args->rx_num_bits++;
98 if (args->lwrx_stats_enable) {
99 args->lwrx_stats[RX_STAT_LOW1_MAX] = std::max(args->lwrx_stats[RX_STAT_LOW1_MAX], dur);
100 args->lwrx_stats[RX_STAT_LOW1_MIN] = std::min(args->lwrx_stats[RX_STAT_LOW1_MIN], dur);
101 args->lwrx_stats[RX_STAT_LOW1_AVE] =
102 args->lwrx_stats[RX_STAT_LOW1_AVE] - (args->lwrx_stats[RX_STAT_LOW1_AVE] >> 4) + dur;
103 }
104 break;
105 case 5: // 1 500->1500
106 // a 1 followed by a 0
107 args->rx_buf[args->rx_num_bytes] = args->rx_buf[args->rx_num_bytes] << 2 | 2;
108 args->rx_num_bits++;
109 args->rx_num_bits++;
110 if (args->lwrx_stats_enable) {
111 args->lwrx_stats[RX_STAT_LOW0_MAX] = std::max(args->lwrx_stats[RX_STAT_LOW0_MAX], dur);
112 args->lwrx_stats[RX_STAT_LOW0_MIN] = std::min(args->lwrx_stats[RX_STAT_LOW0_MIN], dur);
113 args->lwrx_stats[RX_STAT_LOW0_AVE] =
114 args->lwrx_stats[RX_STAT_LOW0_AVE] - (args->lwrx_stats[RX_STAT_LOW0_AVE] >> 4) + dur;
115 }
116 break;
117 default:
118 // not good start again
119 args->rx_state = RX_STATE_IDLE;
120 break;
121 }
122 if (args->rx_num_bits >= 8) {
123 args->rx_num_bytes++;
124 args->rx_num_bits = 0;
125 if (args->rx_num_bytes >= RX_MSGLEN) {
126 uint32_t curr_millis = millis();
127 if (args->rx_repeats > 0) {
128 if ((curr_millis - args->rx_prevpkttime) / 100 > args->rx_timeout) {
129 args->rx_repeatcount = 1;
130 } else {
131 // Test message same as last one
132 int16_t i = RX_MSGLEN; // int
133 do {
134 i--;
135 } while ((i >= 0) && (args->rx_msg[i] == args->rx_buf[i]));
136 if (i < 0) {
137 args->rx_repeatcount++;
138 } else {
139 args->rx_repeatcount = 1;
140 }
141 }
142 } else {
143 args->rx_repeatcount = 0;
144 }
145 args->rx_prevpkttime = curr_millis;
146 // If last message hasn't been read it gets overwritten
147 memcpy(args->rx_msg, args->rx_buf, RX_MSGLEN);
148 if (args->rx_repeats == 0 || args->rx_repeatcount == args->rx_repeats) {
149 if (args->rx_pairtimeout != 0) {
150 if ((curr_millis - args->rx_pairstarttime) / 100 <= args->rx_pairtimeout) {
151 if (args->rx_msg[3] == RX_CMD_ON) {
152 args->rx_addpairfrommsg_();
153 } else if (args->rx_msg[3] == RX_CMD_OFF) {
154 args->rx_remove_pair_(&args->rx_msg[2]);
155 }
156 }
157 }
158 if (args->rx_report_message_()) {
159 args->rx_msgcomplete = true;
160 }
161 args->rx_pairtimeout = 0;
162 }
163 // And cycle round for next one
164 args->rx_state = RX_STATE_IDLE;
165 } else {
166 args->rx_state = RX_STATE_BYTESTARTFOUND;
167 }
168 }
169 break;
170 }
171}
172
176bool LwRx::lwrx_message() { return (this->rx_msgcomplete); }
177
181void LwRx::lwrx_settranslate(bool rxtranslate) { this->rx_translate = rxtranslate; }
185bool LwRx::lwrx_getmessage(uint8_t *buf, uint8_t len) {
186 bool ret = true;
187 int16_t j = 0; // int
188 if (this->rx_msgcomplete && len <= RX_MSGLEN) {
189 // Copy message under interrupt lock to prevent ISR overwriting rx_msg mid-read
190 uint8_t msg_copy[RX_MSGLEN];
191 {
192 InterruptLock lock;
193 memcpy(msg_copy, this->rx_msg, RX_MSGLEN);
194 this->rx_msgcomplete = false;
195 }
196 for (uint8_t i = 0; ret && i < RX_MSGLEN; i++) {
197 if (this->rx_translate || (len != RX_MSGLEN)) {
198 j = this->rx_find_nibble_(msg_copy[i]);
199 if (j < 0)
200 ret = false;
201 } else {
202 j = msg_copy[i];
203 }
204 switch (len) {
205 case 4:
206 if (i == 9)
207 buf[2] = j;
208 if (i == 2)
209 buf[3] = j;
210 [[fallthrough]];
211 case 2:
212 if (i == 3)
213 buf[0] = j;
214 if (i == 0)
215 buf[1] = j << 4;
216 if (i == 1)
217 buf[1] += j;
218 break;
219 case 10:
220 buf[i] = j;
221 break;
222 }
223 }
224 } else {
225 ret = false;
226 }
227 return ret;
228}
229
234
238void LwRx::lwrx_setfilter(uint8_t repeats, uint8_t timeout) {
239 this->rx_repeats = repeats;
240 this->rx_timeout = timeout;
241}
242
248uint8_t LwRx::lwrx_addpair(const uint8_t *pairdata) {
249 if (this->rx_paircount < RX_MAXPAIRS) {
250 for (uint8_t i = 0; i < 8; i++) {
251 this->rx_pairs[rx_paircount][i] = RX_NIBBLE[pairdata[i]];
252 }
253 this->rx_paircommit_();
254 }
255 return this->rx_paircount;
256}
257
261void LwRx::lwrx_makepair(uint8_t timeout) {
262 this->rx_pairtimeout = timeout;
263 this->rx_pairstarttime = millis();
264}
265
269uint8_t LwRx::lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber) {
270 if (pairnumber < this->rx_paircount) {
271 int16_t j; // int
272 for (uint8_t i = 0; i < 8; i++) {
273 j = this->rx_find_nibble_(this->rx_pairs[pairnumber][i]);
274 if (j >= 0)
275 pairdata[i] = j;
276 }
277 }
278 return this->rx_paircount;
279}
280
285
289bool LwRx::lwrx_getstats_(uint16_t *stats) { // unsigned int
290 if (this->lwrx_stats_enable) {
291 memcpy(stats, this->lwrx_stats, 2 * RX_STAT_COUNT);
292 return true;
293 } else {
294 return false;
295 }
296}
297
301void LwRx::lwrx_setstatsenable_(bool rx_stats_enable) {
302 this->lwrx_stats_enable = rx_stats_enable;
303 if (!this->lwrx_stats_enable) {
304 // clear down stats when disabling
305 memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
306 }
307}
311void LwRx::lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only) {
312 this->rx_pairEnforce = pair_enforce;
313 this->rx_pairBaseOnly = pair_base_only;
314}
315
322 // rx_pin = pin;
323 pin->setup();
324 this->rx_pin_isr_ = pin->to_isr();
326
327 memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
328}
329
335 if (this->rx_pairEnforce && this->rx_paircount == 0) {
336 return false;
337 } else {
338 bool all_devices;
339 // True if mood to device 15 or Off cmd with Allof paramater
340 all_devices = ((this->rx_msg[3] == RX_CMD_MOOD && this->rx_msg[2] == RX_DEV_15) ||
341 (this->rx_msg[3] == RX_CMD_OFF && this->rx_msg[0] == RX_PAR0_ALLOFF));
342 return (rx_check_pairs_(&this->rx_msg[2], all_devices) != -1);
343 }
344}
349int16_t LwRx::rx_find_nibble_(uint8_t data) { // int
350 int16_t i = 15; // int
351 do {
352 if (RX_NIBBLE[i] == data)
353 break;
354 i--;
355 } while (i >= 0);
356 return i;
357}
358
363 if (this->rx_paircount < RX_MAXPAIRS) {
364 memcpy(this->rx_pairs[this->rx_paircount], &this->rx_msg[2], 8);
365 this->rx_paircommit_();
366 }
367}
368
373 if (this->rx_paircount == 0 || this->rx_check_pairs_(this->rx_pairs[this->rx_paircount], false) < 0) {
374 this->rx_paircount++;
375 }
376}
377
384int16_t LwRx::rx_check_pairs_(const uint8_t *buf, bool all_devices) { // int
385 if (this->rx_paircount == 0) {
386 return -2;
387 } else {
388 int16_t pair = this->rx_paircount; // int
389 int16_t j = -1; // int
390 int16_t jstart, jend; // int
391 if (this->rx_pairBaseOnly) {
392 // skip room(8) and dev/cmd (0,1)
393 jstart = 7;
394 jend = 2;
395 } else {
396 // include room in comparison
397 jstart = 8;
398 // skip device comparison if allDevices true
399 jend = (all_devices) ? 2 : 0;
400 }
401 while (pair > 0 && j < 0) {
402 pair--;
403 j = jstart;
404 while (j > jend) {
405 j--;
406 if (j != 1) {
407 if (this->rx_pairs[pair][j] != buf[j]) {
408 j = -1;
409 }
410 }
411 }
412 }
413 return (j >= 0) ? pair : -1;
414 }
415}
416
420void LwRx::rx_remove_pair_(uint8_t *buf) {
421 int16_t pair = this->rx_check_pairs_(buf, false); // int
422 if (pair >= 0) {
423 while (pair < this->rx_paircount - 1) {
424 for (uint8_t j = 0; j < 8; j++) {
425 this->rx_pairs[pair][j] = this->rx_pairs[pair + 1][j];
426 }
427 pair++;
428 }
429 this->rx_paircount--;
430 }
431}
432
433} // namespace lightwaverf
434} // namespace esphome
435#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:1791
int16_t rx_find_nibble_(uint8_t data)
Find nibble from byte returns -1 if none found.
Definition LwRx.cpp:349
volatile bool rx_msgcomplete
Definition LwRx.h:108
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:334
void rx_paircommit_()
check and commit pair
Definition LwRx.cpp:372
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:384
bool lwrx_getstats_(uint16_t *stats)
Return stats on high and low pulses.
Definition LwRx.cpp:289
uint32_t lwrx_packetinterval()
Return time in milliseconds since last packet received.
Definition LwRx.cpp:233
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:311
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:321
void rx_addpairfrommsg_()
add pair from message buffer
Definition LwRx.cpp:362
uint8_t lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber)
Get pair data (translated back to nibble form.
Definition LwRx.cpp:269
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:301
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:248
bool lwrx_message()
Test if a message has arrived.
Definition LwRx.cpp:176
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:420
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:21
void lwrx_setfilter(uint8_t repeats, uint8_t timeout)
Set up repeat filtering of received messages.
Definition LwRx.cpp:238
void lwrx_settranslate(bool translate)
Set translate mode.
Definition LwRx.cpp:181
void lwrx_makepair(uint8_t timeout)
Make a pair from next message successfully received.
Definition LwRx.cpp:261
void lwrx_clearpairing_()
Clear all pairing.
Definition LwRx.cpp:284
bool lwrx_getmessage(uint8_t *buf, uint8_t len)
Transfer a message to user buffer.
Definition LwRx.cpp:185
@ INTERRUPT_ANY_EDGE
Definition gpio.h:52
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
const char int const __FlashStringHelper va_list args
Definition log.h:74
std::string size_t len
Definition helpers.h:892
uint32_t IRAM_ATTR HOT micros()
Definition core.cpp:29
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:26
static void uint32_t