ESPHome  2024.12.2
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 
12 namespace esphome {
13 namespace lightwaverf {
14 
20 void 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 
175 bool LwRx::lwrx_message() { return (this->rx_msgcomplete); }
176 
180 void LwRx::lwrx_settranslate(bool rxtranslate) { this->rx_translate = rxtranslate; }
184 bool 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 
225 uint32_t LwRx::lwrx_packetinterval() { return millis() - this->rx_prevpkttime; }
226 
230 void LwRx::lwrx_setfilter(uint8_t repeats, uint8_t timeout) {
231  this->rx_repeats = repeats;
232  this->rx_timeout = timeout;
233 }
234 
240 uint8_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 
253 void LwRx::lwrx_makepair(uint8_t timeout) {
254  this->rx_pairtimeout = timeout;
255  this->rx_pairstarttime = millis();
256 }
257 
261 uint8_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 
281 bool 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 
293 void 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 }
303 void 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 }
341 int16_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 
376 int16_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 
412 void 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
bool lwrx_getmessage(uint8_t *buf, uint8_t len)
Transfer a message to user buffer.
Definition: LwRx.cpp:184
uint32_t rx_pairstarttime
Definition: LwRx.h:100
uint32_t lwrx_packetinterval()
Return time in milliseconds since last packet received.
Definition: LwRx.cpp:225
void rx_addpairfrommsg_()
add pair from message buffer
Definition: LwRx.cpp:354
void lwrx_setstatsenable_(bool rx_stats_enable)
Set stats mode.
Definition: LwRx.cpp:293
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
virtual void setup()=0
void lwrx_setfilter(uint8_t repeats, uint8_t timeout)
Set up repeat filtering of received messages.
Definition: LwRx.cpp:230
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:27
uint8_t rx_buf[RX_MSGLEN]
Definition: LwRx.h:104
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
uint8_t rx_pairs[RX_MAXPAIRS][8]
Definition: LwRx.h:87
bool lwrx_message()
Test if a message has arrived.
Definition: LwRx.cpp:175
uint8_t lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber)
Get pair data (translated back to nibble form.
Definition: LwRx.cpp:261
void lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only)
Set pairs behaviour.
Definition: LwRx.cpp:303
bool lwrx_getstats_(uint16_t *stats)
Return stats on high and low pulses.
Definition: LwRx.cpp:281
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
void lwrx_makepair(uint8_t timeout)
Make a pair from next message successfully received.
Definition: LwRx.cpp:253
virtual ISRInternalGPIOPin to_isr() const =0
std::string size_t len
Definition: helpers.h:293
uint8_t rx_repeatcount
Definition: LwRx.h:97
void lwrx_clearpairing_()
Clear all pairing.
Definition: LwRx.cpp:276
uint32_t rx_prevpkttime
Definition: LwRx.h:99
int16_t rx_find_nibble_(uint8_t data)
Find nibble from byte returns -1 if none found.
Definition: LwRx.cpp:341
uint16_t lwrx_stats[RX_STAT_COUNT]
Definition: LwRx.h:116
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void rx_remove_pair_(uint8_t *buf)
Remove an existing pair matching the buffer.
Definition: LwRx.cpp:412
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
ISRInternalGPIOPin rx_pin_isr_
Definition: LwRx.h:137
void lwrx_setup(InternalGPIOPin *pin)
Set things up to receive LightWaveRF 434Mhz messages pin must be 2 or 3 to trigger interrupts !!! For...
Definition: LwRx.cpp:313
uint8_t rx_pairtimeout
Definition: LwRx.h:93
uint8_t rx_msg[RX_MSGLEN]
Definition: LwRx.h:103
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition: gpio.h:81
void lwrx_settranslate(bool translate)
Set translate mode.
Definition: LwRx.cpp:180
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