1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-24 03:28:20 +00:00
Christian 76ebbfefd2
Integration LightwaveRF switches (#4812)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-09-06 10:33:49 +12:00

428 lines
12 KiB
C++

// LwRx.cpp
//
// LightwaveRF 434MHz receiver interface for Arduino
//
// Author: Bob Tidey (robert@tideys.net)
#ifdef USE_ESP8266
#include "LwRx.h"
#include <cstring>
namespace esphome {
namespace lightwaverf {
/**
Pin change interrupt routine that identifies 1 and 0 LightwaveRF bits
and constructs a message when a valid packet of data is received.
**/
void IRAM_ATTR LwRx::rx_process_bits(LwRx *args) {
uint8_t event = args->rx_pin_isr_.digital_read(); // start setting event to the current value
uint32_t curr = micros(); // the current time in microseconds
uint16_t dur = (curr - args->rx_prev); // unsigned int
args->rx_prev = curr;
// set event based on input and duration of previous pulse
if (dur < 120) { // 120 very short
} else if (dur < 500) { // normal short pulse
event += 2;
} else if (dur < 2000) { // normal long pulse
event += 4;
} else if (dur > 5000) { // gap between messages
event += 6;
} else { // 2000 > 5000
event = 8; // illegal gap
}
// state machine transitions
switch (args->rx_state) {
case RX_STATE_IDLE:
switch (event) {
case 7: // 1 after a message gap
args->rx_state = RX_STATE_MSGSTARTFOUND;
break;
}
break;
case RX_STATE_MSGSTARTFOUND:
switch (event) {
case 2: // 0 160->500
// nothing to do wait for next positive edge
break;
case 3: // 1 160->500
args->rx_num_bytes = 0;
args->rx_state = RX_STATE_BYTESTARTFOUND;
break;
default:
// not good start again
args->rx_state = RX_STATE_IDLE;
break;
}
break;
case RX_STATE_BYTESTARTFOUND:
switch (event) {
case 2: // 0 160->500
// nothing to do wait for next positive edge
break;
case 3: // 1 160->500
args->rx_state = RX_STATE_GETBYTE;
args->rx_num_bits = 0;
break;
case 5: // 0 500->1500
args->rx_state = RX_STATE_GETBYTE;
// Starts with 0 so put this into uint8_t
args->rx_num_bits = 1;
args->rx_buf[args->rx_num_bytes] = 0;
break;
default:
// not good start again
args->rx_state = RX_STATE_IDLE;
break;
}
break;
case RX_STATE_GETBYTE:
switch (event) {
case 2: // 0 160->500
// nothing to do wait for next positive edge but do stats
if (args->lwrx_stats_enable) {
args->lwrx_stats[RX_STAT_HIGH_MAX] = std::max(args->lwrx_stats[RX_STAT_HIGH_MAX], dur);
args->lwrx_stats[RX_STAT_HIGH_MIN] = std::min(args->lwrx_stats[RX_STAT_HIGH_MIN], dur);
args->lwrx_stats[RX_STAT_HIGH_AVE] =
args->lwrx_stats[RX_STAT_HIGH_AVE] - (args->lwrx_stats[RX_STAT_HIGH_AVE] >> 4) + dur;
}
break;
case 3: // 1 160->500
// a single 1
args->rx_buf[args->rx_num_bytes] = args->rx_buf[args->rx_num_bytes] << 1 | 1;
args->rx_num_bits++;
if (args->lwrx_stats_enable) {
args->lwrx_stats[RX_STAT_LOW1_MAX] = std::max(args->lwrx_stats[RX_STAT_LOW1_MAX], dur);
args->lwrx_stats[RX_STAT_LOW1_MIN] = std::min(args->lwrx_stats[RX_STAT_LOW1_MIN], dur);
args->lwrx_stats[RX_STAT_LOW1_AVE] =
args->lwrx_stats[RX_STAT_LOW1_AVE] - (args->lwrx_stats[RX_STAT_LOW1_AVE] >> 4) + dur;
}
break;
case 5: // 1 500->1500
// a 1 followed by a 0
args->rx_buf[args->rx_num_bytes] = args->rx_buf[args->rx_num_bytes] << 2 | 2;
args->rx_num_bits++;
args->rx_num_bits++;
if (args->lwrx_stats_enable) {
args->lwrx_stats[RX_STAT_LOW0_MAX] = std::max(args->lwrx_stats[RX_STAT_LOW0_MAX], dur);
args->lwrx_stats[RX_STAT_LOW0_MIN] = std::min(args->lwrx_stats[RX_STAT_LOW0_MIN], dur);
args->lwrx_stats[RX_STAT_LOW0_AVE] =
args->lwrx_stats[RX_STAT_LOW0_AVE] - (args->lwrx_stats[RX_STAT_LOW0_AVE] >> 4) + dur;
}
break;
default:
// not good start again
args->rx_state = RX_STATE_IDLE;
break;
}
if (args->rx_num_bits >= 8) {
args->rx_num_bytes++;
args->rx_num_bits = 0;
if (args->rx_num_bytes >= RX_MSGLEN) {
uint32_t curr_millis = millis();
if (args->rx_repeats > 0) {
if ((curr_millis - args->rx_prevpkttime) / 100 > args->rx_timeout) {
args->rx_repeatcount = 1;
} else {
// Test message same as last one
int16_t i = RX_MSGLEN; // int
do {
i--;
} while ((i >= 0) && (args->rx_msg[i] == args->rx_buf[i]));
if (i < 0) {
args->rx_repeatcount++;
} else {
args->rx_repeatcount = 1;
}
}
} else {
args->rx_repeatcount = 0;
}
args->rx_prevpkttime = curr_millis;
// If last message hasn't been read it gets overwritten
memcpy(args->rx_msg, args->rx_buf, RX_MSGLEN);
if (args->rx_repeats == 0 || args->rx_repeatcount == args->rx_repeats) {
if (args->rx_pairtimeout != 0) {
if ((curr_millis - args->rx_pairstarttime) / 100 <= args->rx_pairtimeout) {
if (args->rx_msg[3] == RX_CMD_ON) {
args->rx_addpairfrommsg_();
} else if (args->rx_msg[3] == RX_CMD_OFF) {
args->rx_remove_pair_(&args->rx_msg[2]);
}
}
}
if (args->rx_report_message_()) {
args->rx_msgcomplete = true;
}
args->rx_pairtimeout = 0;
}
// And cycle round for next one
args->rx_state = RX_STATE_IDLE;
} else {
args->rx_state = RX_STATE_BYTESTARTFOUND;
}
}
break;
}
}
/**
Test if a message has arrived
**/
bool LwRx::lwrx_message() { return (this->rx_msgcomplete); }
/**
Set translate mode
**/
void LwRx::lwrx_settranslate(bool rxtranslate) { this->rx_translate = rxtranslate; }
/**
Transfer a message to user buffer
**/
bool LwRx::lwrx_getmessage(uint8_t *buf, uint8_t len) {
bool ret = true;
int16_t j = 0; // int
if (this->rx_msgcomplete && len <= RX_MSGLEN) {
for (uint8_t i = 0; ret && i < RX_MSGLEN; i++) {
if (this->rx_translate || (len != RX_MSGLEN)) {
j = this->rx_find_nibble_(this->rx_msg[i]);
if (j < 0)
ret = false;
} else {
j = this->rx_msg[i];
}
switch (len) {
case 4:
if (i == 9)
buf[2] = j;
if (i == 2)
buf[3] = j;
case 2:
if (i == 3)
buf[0] = j;
if (i == 0)
buf[1] = j << 4;
if (i == 1)
buf[1] += j;
break;
case 10:
buf[i] = j;
break;
}
}
this->rx_msgcomplete = false;
} else {
ret = false;
}
return ret;
}
/**
Return time in milliseconds since last packet received
**/
uint32_t LwRx::lwrx_packetinterval() { return millis() - this->rx_prevpkttime; }
/**
Set up repeat filtering of received messages
**/
void LwRx::lwrx_setfilter(uint8_t repeats, uint8_t timeout) {
this->rx_repeats = repeats;
this->rx_timeout = timeout;
}
/**
Add a pair to filter received messages
pairdata is device,dummy,5*addr,room
pairdata is held in translated form to make comparisons quicker
**/
uint8_t LwRx::lwrx_addpair(const uint8_t *pairdata) {
if (this->rx_paircount < RX_MAXPAIRS) {
for (uint8_t i = 0; i < 8; i++) {
this->rx_pairs[rx_paircount][i] = RX_NIBBLE[pairdata[i]];
}
this->rx_paircommit_();
}
return this->rx_paircount;
}
/**
Make a pair from next message successfully received
**/
void LwRx::lwrx_makepair(uint8_t timeout) {
this->rx_pairtimeout = timeout;
this->rx_pairstarttime = millis();
}
/**
Get pair data (translated back to nibble form
**/
uint8_t LwRx::lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber) {
if (pairnumber < this->rx_paircount) {
int16_t j; // int
for (uint8_t i = 0; i < 8; i++) {
j = this->rx_find_nibble_(this->rx_pairs[pairnumber][i]);
if (j >= 0)
pairdata[i] = j;
}
}
return this->rx_paircount;
}
/**
Clear all pairing
**/
void LwRx::lwrx_clearpairing_() { rx_paircount = 0; }
/**
Return stats on high and low pulses
**/
bool LwRx::lwrx_getstats_(uint16_t *stats) { // unsigned int
if (this->lwrx_stats_enable) {
memcpy(stats, this->lwrx_stats, 2 * RX_STAT_COUNT);
return true;
} else {
return false;
}
}
/**
Set stats mode
**/
void LwRx::lwrx_setstatsenable_(bool rx_stats_enable) {
this->lwrx_stats_enable = rx_stats_enable;
if (!this->lwrx_stats_enable) {
// clear down stats when disabling
memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
}
}
/**
Set pairs behaviour
**/
void LwRx::lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only) {
this->rx_pairEnforce = pair_enforce;
this->rx_pairBaseOnly = pair_base_only;
}
/**
Set things up to receive LightWaveRF 434Mhz messages
pin must be 2 or 3 to trigger interrupts
!!! For Spark, any pin will work
**/
void LwRx::lwrx_setup(InternalGPIOPin *pin) {
// rx_pin = pin;
pin->setup();
this->rx_pin_isr_ = pin->to_isr();
pin->attach_interrupt(&LwRx::rx_process_bits, this, gpio::INTERRUPT_ANY_EDGE);
memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
}
/**
Check a message to see if it should be reported under pairing / mood / all off rules
returns -1 if none found
**/
bool LwRx::rx_report_message_() {
if (this->rx_pairEnforce && this->rx_paircount == 0) {
return false;
} else {
bool all_devices;
// True if mood to device 15 or Off cmd with Allof paramater
all_devices = ((this->rx_msg[3] == RX_CMD_MOOD && this->rx_msg[2] == RX_DEV_15) ||
(this->rx_msg[3] == RX_CMD_OFF && this->rx_msg[0] == RX_PAR0_ALLOFF));
return (rx_check_pairs_(&this->rx_msg[2], all_devices) != -1);
}
}
/**
Find nibble from byte
returns -1 if none found
**/
int16_t LwRx::rx_find_nibble_(uint8_t data) { // int
int16_t i = 15; // int
do {
if (RX_NIBBLE[i] == data)
break;
i--;
} while (i >= 0);
return i;
}
/**
add pair from message buffer
**/
void LwRx::rx_addpairfrommsg_() {
if (this->rx_paircount < RX_MAXPAIRS) {
memcpy(this->rx_pairs[this->rx_paircount], &this->rx_msg[2], 8);
this->rx_paircommit_();
}
}
/**
check and commit pair
**/
void LwRx::rx_paircommit_() {
if (this->rx_paircount == 0 || this->rx_check_pairs_(this->rx_pairs[this->rx_paircount], false) < 0) {
this->rx_paircount++;
}
}
/**
Check to see if message matches one of the pairs
if mode is pairBase only then ignore device and room
if allDevices is true then ignore the device number
Returns matching pair number, -1 if not found, -2 if no pairs defined
**/
int16_t LwRx::rx_check_pairs_(const uint8_t *buf, bool all_devices) { // int
if (this->rx_paircount == 0) {
return -2;
} else {
int16_t pair = this->rx_paircount; // int
int16_t j = -1; // int
int16_t jstart, jend; // int
if (this->rx_pairBaseOnly) {
// skip room(8) and dev/cmd (0,1)
jstart = 7;
jend = 2;
} else {
// include room in comparison
jstart = 8;
// skip device comparison if allDevices true
jend = (all_devices) ? 2 : 0;
}
while (pair > 0 && j < 0) {
pair--;
j = jstart;
while (j > jend) {
j--;
if (j != 1) {
if (this->rx_pairs[pair][j] != buf[j]) {
j = -1;
}
}
}
}
return (j >= 0) ? pair : -1;
}
}
/**
Remove an existing pair matching the buffer
**/
void LwRx::rx_remove_pair_(uint8_t *buf) {
int16_t pair = this->rx_check_pairs_(buf, false); // int
if (pair >= 0) {
while (pair < this->rx_paircount - 1) {
for (uint8_t j = 0; j < 8; j++) {
this->rx_pairs[pair][j] = this->rx_pairs[pair + 1][j];
}
pair++;
}
this->rx_paircount--;
}
}
} // namespace lightwaverf
} // namespace esphome
#endif