mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Integration LightwaveRF switches (#4812)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user