Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
8.0 KiB

  1. // -----------------------------------------------------------------------------
  2. // Event Counter Sensor
  3. // Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. // -----------------------------------------------------------------------------
  5. #if SENSOR_SUPPORT && EVENTS_SUPPORT
  6. #pragma once
  7. #include <Arduino.h>
  8. #include "../debug.h"
  9. #include "BaseSensor.h"
  10. // we are bound by usable GPIOs
  11. #define EVENTS_SENSORS_MAX 10
  12. class EventSensor : public BaseSensor {
  13. public:
  14. // ---------------------------------------------------------------------
  15. // Public
  16. // ---------------------------------------------------------------------
  17. EventSensor() {
  18. _count = 2;
  19. _sensor_id = SENSOR_EVENTS_ID;
  20. }
  21. ~EventSensor() {
  22. _enableInterrupts(false);
  23. }
  24. // ---------------------------------------------------------------------
  25. void setGPIO(unsigned char gpio) {
  26. _gpio = gpio;
  27. }
  28. void setPinMode(unsigned char pin_mode) {
  29. _pin_mode = pin_mode;
  30. }
  31. void setInterruptMode(unsigned char interrupt_mode) {
  32. _interrupt_mode = interrupt_mode;
  33. }
  34. void setDebounceTime(unsigned long ms) {
  35. _isr_debounce = microsecondsToClockCycles(ms * 1000);
  36. }
  37. // ---------------------------------------------------------------------
  38. unsigned char getGPIO() {
  39. return _gpio;
  40. }
  41. unsigned char getPinMode() {
  42. return _pin_mode;
  43. }
  44. unsigned char getInterruptMode() {
  45. return _interrupt_mode;
  46. }
  47. unsigned long getDebounceTime() {
  48. return _isr_debounce;
  49. }
  50. // ---------------------------------------------------------------------
  51. // Sensors API
  52. // ---------------------------------------------------------------------
  53. // Initialization method, must be idempotent
  54. // Defined outside the class body
  55. void begin() {
  56. pinMode(_gpio, _pin_mode);
  57. _enableInterrupts(true);
  58. _ready = true;
  59. }
  60. // Descriptive name of the sensor
  61. String description() {
  62. char buffer[20];
  63. snprintf(buffer, sizeof(buffer), "INTERRUPT @ GPIO%d", _gpio);
  64. return String(buffer);
  65. }
  66. // Descriptive name of the slot # index
  67. String description(unsigned char index) {
  68. return description();
  69. };
  70. // Address of the sensor (it could be the GPIO or I2C address)
  71. String address(unsigned char index) {
  72. return String(_gpio);
  73. }
  74. // Type for slot # index
  75. unsigned char type(unsigned char index) {
  76. if (index == 0) return MAGNITUDE_COUNT;
  77. if (index == 1) return MAGNITUDE_EVENT;
  78. return MAGNITUDE_NONE;
  79. }
  80. void pre() override {
  81. ETS_GPIO_INTR_DISABLE();
  82. _current = _counter;
  83. _counter = 0ul;
  84. ETS_GPIO_INTR_ENABLE();
  85. }
  86. double value(unsigned char index) override {
  87. switch (index) {
  88. case 0:
  89. return _current;
  90. case 1:
  91. return (_current > 0) ? 1.0 : 0.0;
  92. default:
  93. return 0.0;
  94. }
  95. }
  96. // Handle interrupt calls from isr[GPIO] functions
  97. // Cannot be nested, since the esp8266/Arduino Core already masks all GPIO handlers before calling this function
  98. void ICACHE_RAM_ATTR handleDebouncedInterrupt() {
  99. // Debounce is based around ccount (32bit value), overflowing every:
  100. // ~53s when F_CPU is 80MHz
  101. // ~26s when F_CPU is 160MHz
  102. // see: cores/esp8266/Arduino.h definitions
  103. //
  104. // To convert to / from normal time values, use:
  105. // - microsecondsToClockCycles(microseconds)
  106. // - clockCyclesToMicroseconds(cycles)
  107. // Since the division operation on this chip is pretty slow,
  108. // avoid doing the conversion here and instead do that at initialization
  109. auto cycles = ESP.getCycleCount();
  110. if (cycles - _isr_last > _isr_debounce) {
  111. _isr_last = cycles;
  112. ++_counter;
  113. }
  114. }
  115. void ICACHE_RAM_ATTR handleInterrupt() {
  116. ++_counter;
  117. }
  118. protected:
  119. // ---------------------------------------------------------------------
  120. // Interrupt management
  121. // ---------------------------------------------------------------------
  122. void _attach(unsigned char gpio, unsigned char mode);
  123. void _detach(unsigned char gpio);
  124. void _enableInterrupts(bool value) {
  125. if (value) {
  126. _detach(_gpio);
  127. _attach(_gpio, _interrupt_mode);
  128. } else {
  129. _detach(_gpio);
  130. }
  131. }
  132. // ---------------------------------------------------------------------
  133. // Protected
  134. // ---------------------------------------------------------------------
  135. unsigned long _counter { 0ul };
  136. unsigned long _current { 0ul };
  137. unsigned long _isr_last { 0ul };
  138. unsigned long _isr_debounce { microsecondsToClockCycles(EVENTS1_DEBOUNCE * 1000) };
  139. int _gpio { GPIO_NONE };
  140. int _pin_mode { INPUT };
  141. int _interrupt_mode { RISING };
  142. };
  143. // -----------------------------------------------------------------------------
  144. // Interrupt helpers
  145. // -----------------------------------------------------------------------------
  146. EventSensor * _event_sensor_instance[EVENTS_SENSORS_MAX] = {nullptr};
  147. void ICACHE_RAM_ATTR _event_sensor_isr(EventSensor* instance) {
  148. if (instance->getDebounceTime()) {
  149. instance->handleDebouncedInterrupt();
  150. } else {
  151. instance->handleInterrupt();
  152. }
  153. }
  154. void ICACHE_RAM_ATTR _event_sensor_isr_0() { _event_sensor_isr(_event_sensor_instance[0]); }
  155. void ICACHE_RAM_ATTR _event_sensor_isr_1() { _event_sensor_isr(_event_sensor_instance[1]); }
  156. void ICACHE_RAM_ATTR _event_sensor_isr_2() { _event_sensor_isr(_event_sensor_instance[2]); }
  157. void ICACHE_RAM_ATTR _event_sensor_isr_3() { _event_sensor_isr(_event_sensor_instance[3]); }
  158. void ICACHE_RAM_ATTR _event_sensor_isr_4() { _event_sensor_isr(_event_sensor_instance[4]); }
  159. void ICACHE_RAM_ATTR _event_sensor_isr_5() { _event_sensor_isr(_event_sensor_instance[5]); }
  160. void ICACHE_RAM_ATTR _event_sensor_isr_12() { _event_sensor_isr(_event_sensor_instance[6]); }
  161. void ICACHE_RAM_ATTR _event_sensor_isr_13() { _event_sensor_isr(_event_sensor_instance[7]); }
  162. void ICACHE_RAM_ATTR _event_sensor_isr_14() { _event_sensor_isr(_event_sensor_instance[8]); }
  163. void ICACHE_RAM_ATTR _event_sensor_isr_15() { _event_sensor_isr(_event_sensor_instance[9]); }
  164. static void (*_event_sensor_isr_list[10])() = {
  165. _event_sensor_isr_0, _event_sensor_isr_1, _event_sensor_isr_2,
  166. _event_sensor_isr_3, _event_sensor_isr_4, _event_sensor_isr_5,
  167. _event_sensor_isr_12, _event_sensor_isr_13, _event_sensor_isr_14,
  168. _event_sensor_isr_15
  169. };
  170. void EventSensor::_attach(unsigned char gpio, unsigned char mode) {
  171. if (!gpioValid(gpio)) return;
  172. unsigned char index = gpio > 5 ? gpio-6 : gpio;
  173. if (_event_sensor_instance[index] == this) return;
  174. if (_event_sensor_instance[index]) detachInterrupt(gpio);
  175. _event_sensor_instance[index] = this;
  176. attachInterrupt(gpio, _event_sensor_isr_list[index], mode);
  177. #if SENSOR_DEBUG
  178. DEBUG_MSG_P(PSTR("[SENSOR] GPIO%d interrupt attached to %s\n"), gpio, this->description().c_str());
  179. #endif
  180. }
  181. void EventSensor::_detach(unsigned char gpio) {
  182. if (!gpioValid(gpio)) return;
  183. unsigned char index = gpio > 5 ? gpio-6 : gpio;
  184. if (_event_sensor_instance[index]) {
  185. detachInterrupt(gpio);
  186. _event_sensor_instance[index] = nullptr;
  187. #if SENSOR_DEBUG
  188. DEBUG_MSG_P(PSTR("[SENSOR] GPIO%d interrupt detached from %s\n"), gpio, _event_sensor_instance[index]->description().c_str());
  189. #endif
  190. }
  191. }
  192. #endif // SENSOR_SUPPORT && EVENTS_SUPPORT