mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 01:07:45 +01:00
Integration LightwaveRF switches (#4812)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
feba9ffdc4
commit
76ebbfefd2
9 changed files with 1094 additions and 0 deletions
|
@ -150,6 +150,7 @@ esphome/components/ledc/* @OttoWinter
|
||||||
esphome/components/libretiny/* @kuba2k2
|
esphome/components/libretiny/* @kuba2k2
|
||||||
esphome/components/libretiny_pwm/* @kuba2k2
|
esphome/components/libretiny_pwm/* @kuba2k2
|
||||||
esphome/components/light/* @esphome/core
|
esphome/components/light/* @esphome/core
|
||||||
|
esphome/components/lightwaverf/* @max246
|
||||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||||
esphome/components/lock/* @esphome/core
|
esphome/components/lock/* @esphome/core
|
||||||
esphome/components/logger/* @esphome/core
|
esphome/components/logger/* @esphome/core
|
||||||
|
|
427
esphome/components/lightwaverf/LwRx.cpp
Normal file
427
esphome/components/lightwaverf/LwRx.cpp
Normal file
|
@ -0,0 +1,427 @@
|
||||||
|
// 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
|
142
esphome/components/lightwaverf/LwRx.h
Normal file
142
esphome/components/lightwaverf/LwRx.h
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace lightwaverf {
|
||||||
|
|
||||||
|
// LwRx.h
|
||||||
|
//
|
||||||
|
// LightwaveRF 434MHz receiver for Arduino
|
||||||
|
//
|
||||||
|
// Author: Bob Tidey (robert@tideys.net)
|
||||||
|
|
||||||
|
static const uint8_t RX_STAT_HIGH_AVE = 0;
|
||||||
|
static const uint8_t RX_STAT_HIGH_MAX = 1;
|
||||||
|
static const uint8_t RX_STAT_HIGH_MIN = 2;
|
||||||
|
static const uint8_t RX_STAT_LOW0_AVE = 3;
|
||||||
|
static const uint8_t RX_STAT_LOW0_MAX = 4;
|
||||||
|
static const uint8_t RX_STAT_LOW0_MIN = 5;
|
||||||
|
static const uint8_t RX_STAT_LOW1_AVE = 6;
|
||||||
|
static const uint8_t RX_STAT_LOW1_MAX = 7;
|
||||||
|
static const uint8_t RX_STAT_LOW1_MIN = 8;
|
||||||
|
static const uint8_t RX_STAT_COUNT = 9;
|
||||||
|
|
||||||
|
// sets maximum number of pairings which can be held
|
||||||
|
static const uint8_t RX_MAXPAIRS = 10;
|
||||||
|
|
||||||
|
static const uint8_t RX_NIBBLE[] = {0xF6, 0xEE, 0xED, 0xEB, 0xDE, 0xDD, 0xDB, 0xBE,
|
||||||
|
0xBD, 0xBB, 0xB7, 0x7E, 0x7D, 0x7B, 0x77, 0x6F};
|
||||||
|
static const uint8_t RX_CMD_OFF = 0xF6; // raw 0
|
||||||
|
static const uint8_t RX_CMD_ON = 0xEE; // raw 1
|
||||||
|
static const uint8_t RX_CMD_MOOD = 0xED; // raw 2
|
||||||
|
static const uint8_t RX_PAR0_ALLOFF = 0x7D; // param 192-255 all off (12 in msb)
|
||||||
|
static const uint8_t RX_DEV_15 = 0x6F; // device 15
|
||||||
|
|
||||||
|
static const uint8_t RX_MSGLEN = 10; // expected length of rx message
|
||||||
|
|
||||||
|
static const uint8_t RX_STATE_IDLE = 0;
|
||||||
|
static const uint8_t RX_STATE_MSGSTARTFOUND = 1;
|
||||||
|
static const uint8_t RX_STATE_BYTESTARTFOUND = 2;
|
||||||
|
static const uint8_t RX_STATE_GETBYTE = 3;
|
||||||
|
|
||||||
|
// Gather stats for pulse widths (ave is x 16)
|
||||||
|
static const uint16_t LWRX_STATSDFLT[RX_STAT_COUNT] = {5000, 0, 5000, 20000, 0, 2500, 4000, 0, 500}; // usigned int
|
||||||
|
|
||||||
|
class LwRx {
|
||||||
|
public:
|
||||||
|
// Seup must be called once, set up pin used to receive data
|
||||||
|
void lwrx_setup(InternalGPIOPin *pin);
|
||||||
|
|
||||||
|
// Set translate to determine whether translating from nibbles to bytes in message
|
||||||
|
// Translate off only applies to 10char message returns
|
||||||
|
void lwrx_settranslate(bool translate);
|
||||||
|
|
||||||
|
// Check to see whether message available
|
||||||
|
bool lwrx_message();
|
||||||
|
|
||||||
|
// Get a message, len controls format (2 cmd+param, 4 cmd+param+room+device),10 full message
|
||||||
|
bool lwrx_getmessage(uint8_t *buf, uint8_t len);
|
||||||
|
|
||||||
|
// Setup repeat filter
|
||||||
|
void lwrx_setfilter(uint8_t repeats, uint8_t timeout);
|
||||||
|
|
||||||
|
// Add pair, if no pairing set then all messages are received, returns number of pairs
|
||||||
|
uint8_t lwrx_addpair(const uint8_t *pairdata);
|
||||||
|
|
||||||
|
// Get pair data into buffer for the pairnumber. Returns current paircount
|
||||||
|
// Use pairnumber 255 to just get current paircount
|
||||||
|
uint8_t lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber);
|
||||||
|
|
||||||
|
// Make a pair from next message received within timeout 100mSec
|
||||||
|
// This call returns immediately whilst message checking continues
|
||||||
|
void lwrx_makepair(uint8_t timeout);
|
||||||
|
|
||||||
|
// Set pair mode controls
|
||||||
|
void lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only);
|
||||||
|
|
||||||
|
// Returns time from last packet received in msec
|
||||||
|
// Can be used to determine if Rx may be still receiving repeats
|
||||||
|
uint32_t lwrx_packetinterval();
|
||||||
|
|
||||||
|
static void rx_process_bits(LwRx *arg);
|
||||||
|
|
||||||
|
// Pairing data
|
||||||
|
uint8_t rx_paircount = 0;
|
||||||
|
uint8_t rx_pairs[RX_MAXPAIRS][8];
|
||||||
|
// set false to responds to all messages if no pairs set up
|
||||||
|
bool rx_pairEnforce = false;
|
||||||
|
// set false to use Address, Room and Device in pairs, true just the Address part
|
||||||
|
bool rx_pairBaseOnly = false;
|
||||||
|
|
||||||
|
uint8_t rx_pairtimeout = 0; // 100msec units
|
||||||
|
|
||||||
|
// Repeat filters
|
||||||
|
uint8_t rx_repeats = 2; // msg must be repeated at least this number of times
|
||||||
|
uint8_t rx_repeatcount = 0;
|
||||||
|
uint8_t rx_timeout = 20; // reset repeat window after this in 100mSecs
|
||||||
|
uint32_t rx_prevpkttime = 0; // last packet time in milliseconds
|
||||||
|
uint32_t rx_pairstarttime = 0; // last msg time in milliseconds
|
||||||
|
|
||||||
|
// Receive mode constants and variables
|
||||||
|
uint8_t rx_msg[RX_MSGLEN]; // raw message received
|
||||||
|
uint8_t rx_buf[RX_MSGLEN]; // message buffer during reception
|
||||||
|
|
||||||
|
uint32_t rx_prev; // time of previous interrupt in microseconds
|
||||||
|
|
||||||
|
bool rx_msgcomplete = false; // set high when message available
|
||||||
|
bool rx_translate = true; // Set false to get raw data
|
||||||
|
|
||||||
|
uint8_t rx_state = 0;
|
||||||
|
|
||||||
|
uint8_t rx_num_bits = 0; // number of bits in the current uint8_t
|
||||||
|
uint8_t rx_num_bytes = 0; // number of bytes received
|
||||||
|
|
||||||
|
uint16_t lwrx_stats[RX_STAT_COUNT]; // unsigned int
|
||||||
|
|
||||||
|
bool lwrx_stats_enable = true;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void lwrx_clearpairing_();
|
||||||
|
|
||||||
|
// Return stats on pulse timings
|
||||||
|
bool lwrx_getstats_(uint16_t *stats);
|
||||||
|
|
||||||
|
// Enable collection of stats on pulse timings
|
||||||
|
void lwrx_setstatsenable_(bool rx_stats_enable);
|
||||||
|
|
||||||
|
// internal support functions
|
||||||
|
bool rx_report_message_();
|
||||||
|
int16_t rx_find_nibble_(uint8_t data); // int
|
||||||
|
void rx_addpairfrommsg_();
|
||||||
|
void rx_paircommit_();
|
||||||
|
void rx_remove_pair_(uint8_t *buf);
|
||||||
|
int16_t rx_check_pairs_(const uint8_t *buf, bool all_devices); // int
|
||||||
|
|
||||||
|
ISRInternalGPIOPin rx_pin_isr_;
|
||||||
|
InternalGPIOPin *rx_pin_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lightwaverf
|
||||||
|
} // namespace esphome
|
208
esphome/components/lightwaverf/LwTx.cpp
Normal file
208
esphome/components/lightwaverf/LwTx.cpp
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
// LwTx.cpp
|
||||||
|
//
|
||||||
|
// LightwaveRF 434MHz tx interface for Arduino
|
||||||
|
//
|
||||||
|
// Author: Bob Tidey (robert@tideys.net)
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
|
||||||
|
#include "LwTx.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace lightwaverf {
|
||||||
|
|
||||||
|
static const uint8_t TX_NIBBLE[] = {0xF6, 0xEE, 0xED, 0xEB, 0xDE, 0xDD, 0xDB, 0xBE,
|
||||||
|
0xBD, 0xBB, 0xB7, 0x7E, 0x7D, 0x7B, 0x77, 0x6F};
|
||||||
|
|
||||||
|
static const uint8_t TX_STATE_IDLE = 0;
|
||||||
|
static const uint8_t TX_STATE_MSGSTART = 1;
|
||||||
|
static const uint8_t TX_STATE_BYTESTART = 2;
|
||||||
|
static const uint8_t TX_STATE_SENDBYTE = 3;
|
||||||
|
static const uint8_t TX_STATE_MSGEND = 4;
|
||||||
|
static const uint8_t TX_STATE_GAPSTART = 5;
|
||||||
|
static const uint8_t TX_STATE_GAPEND = 6;
|
||||||
|
/**
|
||||||
|
Set translate mode
|
||||||
|
**/
|
||||||
|
void LwTx::lwtx_settranslate(bool txtranslate) { tx_translate = txtranslate; }
|
||||||
|
|
||||||
|
static void IRAM_ATTR isr_t_xtimer(LwTx *arg) {
|
||||||
|
// Set low after toggle count interrupts
|
||||||
|
arg->tx_toggle_count--;
|
||||||
|
if (arg->tx_toggle_count == arg->tx_trail_count) {
|
||||||
|
// ESP_LOGD("lightwaverf.sensor", "timer")
|
||||||
|
arg->tx_pin->digital_write(arg->txoff);
|
||||||
|
} else if (arg->tx_toggle_count == 0) {
|
||||||
|
arg->tx_toggle_count = arg->tx_high_count; // default high pulse duration
|
||||||
|
switch (arg->tx_state) {
|
||||||
|
case TX_STATE_IDLE:
|
||||||
|
if (arg->tx_msg_active) {
|
||||||
|
arg->tx_repeat = 0;
|
||||||
|
arg->tx_state = TX_STATE_MSGSTART;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TX_STATE_MSGSTART:
|
||||||
|
arg->tx_pin->digital_write(arg->txon);
|
||||||
|
arg->tx_num_bytes = 0;
|
||||||
|
arg->tx_state = TX_STATE_BYTESTART;
|
||||||
|
break;
|
||||||
|
case TX_STATE_BYTESTART:
|
||||||
|
arg->tx_pin->digital_write(arg->txon);
|
||||||
|
arg->tx_bit_mask = 0x80;
|
||||||
|
arg->tx_state = TX_STATE_SENDBYTE;
|
||||||
|
break;
|
||||||
|
case TX_STATE_SENDBYTE:
|
||||||
|
if (arg->tx_buf[arg->tx_num_bytes] & arg->tx_bit_mask) {
|
||||||
|
arg->tx_pin->digital_write(arg->txon);
|
||||||
|
} else {
|
||||||
|
// toggle count for the 0 pulse
|
||||||
|
arg->tx_toggle_count = arg->tx_low_count;
|
||||||
|
}
|
||||||
|
arg->tx_bit_mask >>= 1;
|
||||||
|
if (arg->tx_bit_mask == 0) {
|
||||||
|
arg->tx_num_bytes++;
|
||||||
|
if (arg->tx_num_bytes >= esphome::lightwaverf::LwTx::TX_MSGLEN) {
|
||||||
|
arg->tx_state = TX_STATE_MSGEND;
|
||||||
|
} else {
|
||||||
|
arg->tx_state = TX_STATE_BYTESTART;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TX_STATE_MSGEND:
|
||||||
|
arg->tx_pin->digital_write(arg->txon);
|
||||||
|
arg->tx_state = TX_STATE_GAPSTART;
|
||||||
|
arg->tx_gap_repeat = arg->tx_gap_multiplier;
|
||||||
|
break;
|
||||||
|
case TX_STATE_GAPSTART:
|
||||||
|
arg->tx_toggle_count = arg->tx_gap_count;
|
||||||
|
if (arg->tx_gap_repeat == 0) {
|
||||||
|
arg->tx_state = TX_STATE_GAPEND;
|
||||||
|
} else {
|
||||||
|
arg->tx_gap_repeat--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TX_STATE_GAPEND:
|
||||||
|
arg->tx_repeat++;
|
||||||
|
if (arg->tx_repeat >= arg->tx_repeats) {
|
||||||
|
// disable timer nterrupt
|
||||||
|
arg->lw_timer_stop();
|
||||||
|
arg->tx_msg_active = false;
|
||||||
|
arg->tx_state = TX_STATE_IDLE;
|
||||||
|
} else {
|
||||||
|
arg->tx_state = TX_STATE_MSGSTART;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check for send free
|
||||||
|
**/
|
||||||
|
bool LwTx::lwtx_free() { return !this->tx_msg_active; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
Send a LightwaveRF message (10 nibbles in bytes)
|
||||||
|
**/
|
||||||
|
void LwTx::lwtx_send(const std::vector<uint8_t> &msg) {
|
||||||
|
if (this->tx_translate) {
|
||||||
|
for (uint8_t i = 0; i < TX_MSGLEN; i++) {
|
||||||
|
this->tx_buf[i] = TX_NIBBLE[msg[i] & 0xF];
|
||||||
|
ESP_LOGD("lightwaverf.sensor", "%x ", msg[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// memcpy(tx_buf, msg, tx_msglen);
|
||||||
|
}
|
||||||
|
this->lw_timer_start();
|
||||||
|
this->tx_msg_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Set 5 char address for future messages
|
||||||
|
**/
|
||||||
|
void LwTx::lwtx_setaddr(const uint8_t *addr) {
|
||||||
|
for (uint8_t i = 0; i < 5; i++) {
|
||||||
|
this->tx_buf[i + 4] = TX_NIBBLE[addr[i] & 0xF];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Send a LightwaveRF message (10 nibbles in bytes)
|
||||||
|
**/
|
||||||
|
void LwTx::lwtx_cmd(uint8_t command, uint8_t parameter, uint8_t room, uint8_t device) {
|
||||||
|
// enable timer 2 interrupts
|
||||||
|
this->tx_buf[0] = TX_NIBBLE[parameter >> 4];
|
||||||
|
this->tx_buf[1] = TX_NIBBLE[parameter & 0xF];
|
||||||
|
this->tx_buf[2] = TX_NIBBLE[device & 0xF];
|
||||||
|
this->tx_buf[3] = TX_NIBBLE[command & 0xF];
|
||||||
|
this->tx_buf[9] = TX_NIBBLE[room & 0xF];
|
||||||
|
this->lw_timer_start();
|
||||||
|
this->tx_msg_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Set things up to transmit LightWaveRF 434Mhz messages
|
||||||
|
**/
|
||||||
|
void LwTx::lwtx_setup(InternalGPIOPin *pin, uint8_t repeats, bool inverted, int u_sec) {
|
||||||
|
pin->setup();
|
||||||
|
tx_pin = pin;
|
||||||
|
|
||||||
|
tx_pin->pin_mode(gpio::FLAG_OUTPUT);
|
||||||
|
tx_pin->digital_write(txoff);
|
||||||
|
|
||||||
|
if (repeats > 0 && repeats < 40) {
|
||||||
|
this->tx_repeats = repeats;
|
||||||
|
}
|
||||||
|
if (inverted) {
|
||||||
|
this->txon = 0;
|
||||||
|
this->txoff = 1;
|
||||||
|
} else {
|
||||||
|
this->txon = 1;
|
||||||
|
this->txoff = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int period1 = 330;
|
||||||
|
/*
|
||||||
|
if (period > 32 && period < 1000) {
|
||||||
|
period1 = period;
|
||||||
|
} else {
|
||||||
|
// default 330 uSec
|
||||||
|
period1 = 330;
|
||||||
|
}*/
|
||||||
|
this->espPeriod = 5 * period1;
|
||||||
|
timer1_isr_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LwTx::lwtx_set_tick_counts(uint8_t low_count, uint8_t high_count, uint8_t trail_count, uint8_t gap_count) {
|
||||||
|
this->tx_low_count = low_count;
|
||||||
|
this->tx_high_count = high_count;
|
||||||
|
this->tx_trail_count = trail_count;
|
||||||
|
this->tx_gap_count = gap_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LwTx::lwtx_set_gap_multiplier(uint8_t gap_multiplier) { this->tx_gap_multiplier = gap_multiplier; }
|
||||||
|
|
||||||
|
void LwTx::lw_timer_start() {
|
||||||
|
{
|
||||||
|
InterruptLock lock;
|
||||||
|
static LwTx *arg = this; // NOLINT
|
||||||
|
timer1_attachInterrupt([] { isr_t_xtimer(arg); });
|
||||||
|
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
|
||||||
|
timer1_write(this->espPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LwTx::lw_timer_stop() {
|
||||||
|
{
|
||||||
|
InterruptLock lock;
|
||||||
|
timer1_disable();
|
||||||
|
timer1_detachInterrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lightwaverf
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
92
esphome/components/lightwaverf/LwTx.h
Normal file
92
esphome/components/lightwaverf/LwTx.h
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace lightwaverf {
|
||||||
|
|
||||||
|
// LxTx.h
|
||||||
|
//
|
||||||
|
// LightwaveRF 434MHz tx interface for Arduino
|
||||||
|
//
|
||||||
|
// Author: Bob Tidey (robert@tideys.net)
|
||||||
|
|
||||||
|
// Include basic library header and set default TX pin
|
||||||
|
static const uint8_t TX_PIN_DEFAULT = 13;
|
||||||
|
|
||||||
|
class LwTx {
|
||||||
|
public:
|
||||||
|
// Sets up basic parameters must be called at least once
|
||||||
|
void lwtx_setup(InternalGPIOPin *pin, uint8_t repeats, bool inverted, int u_sec);
|
||||||
|
|
||||||
|
// Allows changing basic tick counts from their defaults
|
||||||
|
void lwtx_set_tick_counts(uint8_t low_count, uint8_t high_count, uint8_t trail_count, uint8_t gap_count);
|
||||||
|
|
||||||
|
// Allws multiplying the gap period for creating very large gaps
|
||||||
|
void lwtx_set_gap_multiplier(uint8_t gap_multiplier);
|
||||||
|
|
||||||
|
// determines whether incoming data or should be translated from nibble data
|
||||||
|
void lwtx_settranslate(bool txtranslate);
|
||||||
|
|
||||||
|
// Checks whether tx is free to accept a new message
|
||||||
|
bool lwtx_free();
|
||||||
|
|
||||||
|
// Basic send of new 10 char message, not normally needed if setaddr and cmd are used.
|
||||||
|
void lwtx_send(const std::vector<uint8_t> &msg);
|
||||||
|
|
||||||
|
// Sets up 5 char address which will be used to form messages for lwtx_cmd
|
||||||
|
void lwtx_setaddr(const uint8_t *addr);
|
||||||
|
|
||||||
|
// Send Command
|
||||||
|
void lwtx_cmd(uint8_t command, uint8_t parameter, uint8_t room, uint8_t device);
|
||||||
|
|
||||||
|
// Allows changing basic tick counts from their defaults
|
||||||
|
void lw_timer_start();
|
||||||
|
|
||||||
|
// Allws multiplying the gap period for creating very large gaps
|
||||||
|
void lw_timer_stop();
|
||||||
|
|
||||||
|
// These set the pulse durationlws in ticks. ESP uses 330uSec base tick, else use 140uSec
|
||||||
|
uint8_t tx_low_count = 3; // total number of ticks in a low (990 uSec)
|
||||||
|
uint8_t tx_high_count = 2; // total number of ticks in a high (660 uSec)
|
||||||
|
uint8_t tx_trail_count = 1; // tick count to set line low (330 uSec)
|
||||||
|
|
||||||
|
uint8_t tx_toggle_count = 3;
|
||||||
|
|
||||||
|
static const uint8_t TX_MSGLEN = 10; // the expected length of the message
|
||||||
|
|
||||||
|
// Transmit mode constants and variables
|
||||||
|
uint8_t tx_repeats = 12; // Number of repeats of message sent
|
||||||
|
uint8_t txon = 1;
|
||||||
|
uint8_t txoff = 0;
|
||||||
|
bool tx_msg_active = false; // set true to activate message sending
|
||||||
|
bool tx_translate = true; // Set false to send raw data
|
||||||
|
|
||||||
|
uint8_t tx_buf[TX_MSGLEN]; // the message buffer during reception
|
||||||
|
uint8_t tx_repeat = 0; // counter for repeats
|
||||||
|
uint8_t tx_state = 0;
|
||||||
|
uint16_t tx_gap_repeat = 0; // unsigned int
|
||||||
|
|
||||||
|
// Use with low repeat counts
|
||||||
|
uint8_t tx_gap_count = 33; // Inter-message gap count (10.9 msec)
|
||||||
|
uint32_t espPeriod = 0; // Holds interrupt timer0 period
|
||||||
|
uint32_t espNext = 0; // Holds interrupt next count
|
||||||
|
|
||||||
|
// Gap multiplier byte is used to multiply gap if longer periods are needed for experimentation
|
||||||
|
// If gap is 255 (35msec) then this to give a max of 9 seconds
|
||||||
|
// Used with low repeat counts to find if device times out
|
||||||
|
uint8_t tx_gap_multiplier = 0; // Gap extension byte
|
||||||
|
|
||||||
|
uint8_t tx_bit_mask = 0; // bit mask in current byte
|
||||||
|
uint8_t tx_num_bytes = 0; // number of bytes sent
|
||||||
|
|
||||||
|
InternalGPIOPin *tx_pin;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t duty_on_;
|
||||||
|
uint32_t duty_off_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lightwaverf
|
||||||
|
} // namespace esphome
|
83
esphome/components/lightwaverf/__init__.py
Normal file
83
esphome/components/lightwaverf/__init__.py
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import pins
|
||||||
|
from esphome import automation
|
||||||
|
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_READ_PIN,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_WRITE_PIN,
|
||||||
|
CONF_REPEAT,
|
||||||
|
CONF_INVERTED,
|
||||||
|
CONF_PULSE_LENGTH,
|
||||||
|
CONF_CODE,
|
||||||
|
)
|
||||||
|
from esphome.cpp_helpers import gpio_pin_expression
|
||||||
|
|
||||||
|
CODEOWNERS = ["@max246"]
|
||||||
|
|
||||||
|
lightwaverf_ns = cg.esphome_ns.namespace("lightwaverf")
|
||||||
|
|
||||||
|
|
||||||
|
LIGHTWAVERFComponent = lightwaverf_ns.class_(
|
||||||
|
"LightWaveRF", cg.Component, cg.PollingComponent
|
||||||
|
)
|
||||||
|
LightwaveRawAction = lightwaverf_ns.class_("SendRawAction", automation.Action)
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(LIGHTWAVERFComponent),
|
||||||
|
cv.Optional(CONF_READ_PIN, default=13): pins.internal_gpio_input_pin_schema,
|
||||||
|
cv.Optional(CONF_WRITE_PIN, default=14): pins.internal_gpio_input_pin_schema,
|
||||||
|
}
|
||||||
|
).extend(cv.polling_component_schema("1s"))
|
||||||
|
|
||||||
|
|
||||||
|
LIGHTWAVE_SEND_SCHEMA = cv.Any(
|
||||||
|
cv.int_range(min=1),
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(LIGHTWAVERFComponent),
|
||||||
|
cv.Required(CONF_NAME): cv.string,
|
||||||
|
cv.Required(CONF_CODE): cv.All(
|
||||||
|
[cv.Any(cv.hex_uint8_t)],
|
||||||
|
cv.Length(min=10),
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_REPEAT, default=10): cv.int_,
|
||||||
|
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_PULSE_LENGTH, default=330): cv.int_,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"lightwaverf.send_raw",
|
||||||
|
LightwaveRawAction,
|
||||||
|
LIGHTWAVE_SEND_SCHEMA,
|
||||||
|
)
|
||||||
|
async def send_raw_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
|
||||||
|
repeats = await cg.templatable(config[CONF_REPEAT], args, int)
|
||||||
|
inverted = await cg.templatable(config[CONF_INVERTED], args, bool)
|
||||||
|
pulse_length = await cg.templatable(config[CONF_PULSE_LENGTH], args, int)
|
||||||
|
code = config[CONF_CODE]
|
||||||
|
|
||||||
|
cg.add(var.set_repeats(repeats))
|
||||||
|
cg.add(var.set_inverted(inverted))
|
||||||
|
cg.add(var.set_pulse_length(pulse_length))
|
||||||
|
cg.add(var.set_data(code))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
pin_read = await gpio_pin_expression(config[CONF_READ_PIN])
|
||||||
|
pin_write = await gpio_pin_expression(config[CONF_WRITE_PIN])
|
||||||
|
cg.add(var.set_pin(pin_write, pin_read))
|
67
esphome/components/lightwaverf/lightwaverf.cpp
Normal file
67
esphome/components/lightwaverf/lightwaverf.cpp
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
|
||||||
|
#include "lightwaverf.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace lightwaverf {
|
||||||
|
|
||||||
|
static const char *const TAG = "lightwaverf.sensor";
|
||||||
|
|
||||||
|
static const uint8_t DEFAULT_REPEAT = 10;
|
||||||
|
static const bool DEFAULT_INVERT = false;
|
||||||
|
static const uint32_t DEFAULT_TICK = 330;
|
||||||
|
|
||||||
|
void LightWaveRF::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up Lightwave RF...");
|
||||||
|
|
||||||
|
this->lwtx_.lwtx_setup(pin_tx_, DEFAULT_REPEAT, DEFAULT_INVERT, DEFAULT_TICK);
|
||||||
|
this->lwrx_.lwrx_setup(pin_rx_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightWaveRF::update() { this->read_tx(); }
|
||||||
|
|
||||||
|
void LightWaveRF::read_tx() {
|
||||||
|
if (this->lwrx_.lwrx_message()) {
|
||||||
|
this->lwrx_.lwrx_getmessage(msg_, msglen_);
|
||||||
|
print_msg_(msg_, msglen_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightWaveRF::send_rx(const std::vector<uint8_t> &msg, uint8_t repeats, bool inverted, int u_sec) {
|
||||||
|
this->lwtx_.lwtx_setup(pin_tx_, repeats, inverted, u_sec);
|
||||||
|
|
||||||
|
uint32_t timeout = 0;
|
||||||
|
if (this->lwtx_.lwtx_free()) {
|
||||||
|
this->lwtx_.lwtx_send(msg);
|
||||||
|
timeout = millis();
|
||||||
|
ESP_LOGD(TAG, "[%i] msg start", timeout);
|
||||||
|
}
|
||||||
|
while (!this->lwtx_.lwtx_free() && millis() < (timeout + 1000)) {
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
timeout = millis() - timeout;
|
||||||
|
ESP_LOGD(TAG, "[%u] msg sent: %i", millis(), timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightWaveRF::print_msg_(uint8_t *msg, uint8_t len) {
|
||||||
|
char buffer[65];
|
||||||
|
ESP_LOGD(TAG, " Received code (len:%i): ", len);
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
sprintf(&buffer[i * 6], "0x%02x, ", msg[i]);
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "[%s]", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightWaveRF::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Lightwave RF:");
|
||||||
|
LOG_PIN(" Pin TX: ", this->pin_tx_);
|
||||||
|
LOG_PIN(" Pin RX: ", this->pin_rx_);
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
}
|
||||||
|
} // namespace lightwaverf
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
70
esphome/components/lightwaverf/lightwaverf.h
Normal file
70
esphome/components/lightwaverf/lightwaverf.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "LwRx.h"
|
||||||
|
#include "LwTx.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace lightwaverf {
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
|
||||||
|
class LightWaveRF : public PollingComponent {
|
||||||
|
public:
|
||||||
|
void set_pin(InternalGPIOPin *pin_tx, InternalGPIOPin *pin_rx) {
|
||||||
|
pin_tx_ = pin_tx;
|
||||||
|
pin_rx_ = pin_rx;
|
||||||
|
}
|
||||||
|
void update() override;
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
void read_tx();
|
||||||
|
void send_rx(const std::vector<uint8_t> &msg, uint8_t repeats, bool inverted, int u_sec);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void print_msg_(uint8_t *msg, uint8_t len);
|
||||||
|
uint8_t msg_[10];
|
||||||
|
uint8_t msglen_ = 10;
|
||||||
|
InternalGPIOPin *pin_tx_;
|
||||||
|
InternalGPIOPin *pin_rx_;
|
||||||
|
LwRx lwrx_;
|
||||||
|
LwTx lwtx_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class SendRawAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
SendRawAction(LightWaveRF *parent) : parent_(parent){};
|
||||||
|
TEMPLATABLE_VALUE(int, repeat);
|
||||||
|
TEMPLATABLE_VALUE(int, inverted);
|
||||||
|
TEMPLATABLE_VALUE(int, pulse_length);
|
||||||
|
TEMPLATABLE_VALUE(std::vector<uint8_t>, code);
|
||||||
|
|
||||||
|
void set_repeats(const int &data) { repeat_ = data; }
|
||||||
|
void set_inverted(const int &data) { inverted_ = data; }
|
||||||
|
void set_pulse_length(const int &data) { pulse_length_ = data; }
|
||||||
|
void set_data(const std::vector<uint8_t> &data) { code_ = data; }
|
||||||
|
|
||||||
|
void play(Ts... x) {
|
||||||
|
int repeats = this->repeat_.value(x...);
|
||||||
|
int inverted = this->inverted_.value(x...);
|
||||||
|
int pulse_length = this->pulse_length_.value(x...);
|
||||||
|
std::vector<uint8_t> msg = this->code_.value(x...);
|
||||||
|
|
||||||
|
this->parent_->send_rx(msg, repeats, inverted, pulse_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LightWaveRF *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
} // namespace lightwaverf
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
|
@ -1188,6 +1188,10 @@ qr_code:
|
||||||
- id: homepage_qr
|
- id: homepage_qr
|
||||||
value: https://esphome.io/index.html
|
value: https://esphome.io/index.html
|
||||||
|
|
||||||
|
lightwaverf:
|
||||||
|
read_pin: 13
|
||||||
|
write_pin: 14
|
||||||
|
|
||||||
alarm_control_panel:
|
alarm_control_panel:
|
||||||
- platform: template
|
- platform: template
|
||||||
id: alarmcontrolpanel1
|
id: alarmcontrolpanel1
|
||||||
|
|
Loading…
Reference in a new issue