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.

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