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. _last = _current;
  82. _current = _counter;
  83. }
  84. double value(unsigned char index) override {
  85. switch (index) {
  86. case 0:
  87. return _current - _last;
  88. case 1:
  89. return (_current - _last > 0) ? 1.0 : 0.0;
  90. default:
  91. return 0.0;
  92. }
  93. }
  94. // Handle interrupt calls from isr[GPIO] functions
  95. // Cannot be nested, since the esp8266/Arduino Core already masks all GPIO handlers before calling this function
  96. void ICACHE_RAM_ATTR handleDebouncedInterrupt() {
  97. // Debounce is based around ccount (32bit value), overflowing every:
  98. // ~53s when F_CPU is 80MHz
  99. // ~26s when F_CPU is 160MHz
  100. // see: cores/esp8266/Arduino.h definitions
  101. //
  102. // To convert to / from normal time values, use:
  103. // - microsecondsToClockCycles(microseconds)
  104. // - clockCyclesToMicroseconds(cycles)
  105. // Since the division operation on this chip is pretty slow,
  106. // avoid doing the conversion here and instead do that at initialization
  107. auto cycles = ESP.getCycleCount();
  108. if (cycles - _isr_last > _isr_debounce) {
  109. _isr_last = cycles;
  110. ++_counter;
  111. }
  112. }
  113. void ICACHE_RAM_ATTR handleInterrupt() {
  114. ++_counter;
  115. }
  116. protected:
  117. // ---------------------------------------------------------------------
  118. // Interrupt management
  119. // ---------------------------------------------------------------------
  120. void _attach(unsigned char gpio, unsigned char mode);
  121. void _detach(unsigned char gpio);
  122. void _enableInterrupts(bool value) {
  123. if (value) {
  124. _detach(_gpio);
  125. _attach(_gpio, _interrupt_mode);
  126. } else {
  127. _detach(_gpio);
  128. }
  129. }
  130. // ---------------------------------------------------------------------
  131. // Protected
  132. // ---------------------------------------------------------------------
  133. unsigned long _counter { 0ul };
  134. unsigned long _current { 0ul };
  135. unsigned long _last { 0ul };
  136. unsigned long _isr_last { 0ul };
  137. unsigned long _isr_debounce { microsecondsToClockCycles(EVENTS1_DEBOUNCE * 1000) };
  138. int _gpio { GPIO_NONE };
  139. int _pin_mode { INPUT };
  140. int _interrupt_mode { RISING };
  141. };
  142. // -----------------------------------------------------------------------------
  143. // Interrupt helpers
  144. // -----------------------------------------------------------------------------
  145. EventSensor * _event_sensor_instance[EVENTS_SENSORS_MAX] = {nullptr};
  146. void ICACHE_RAM_ATTR _event_sensor_isr(EventSensor* instance) {
  147. if (instance->getDebounceTime()) {
  148. instance->handleDebouncedInterrupt();
  149. } else {
  150. instance->handleInterrupt();
  151. }
  152. }
  153. void ICACHE_RAM_ATTR _event_sensor_isr_0() { _event_sensor_isr(_event_sensor_instance[0]); }
  154. void ICACHE_RAM_ATTR _event_sensor_isr_1() { _event_sensor_isr(_event_sensor_instance[1]); }
  155. void ICACHE_RAM_ATTR _event_sensor_isr_2() { _event_sensor_isr(_event_sensor_instance[2]); }
  156. void ICACHE_RAM_ATTR _event_sensor_isr_3() { _event_sensor_isr(_event_sensor_instance[3]); }
  157. void ICACHE_RAM_ATTR _event_sensor_isr_4() { _event_sensor_isr(_event_sensor_instance[4]); }
  158. void ICACHE_RAM_ATTR _event_sensor_isr_5() { _event_sensor_isr(_event_sensor_instance[5]); }
  159. void ICACHE_RAM_ATTR _event_sensor_isr_12() { _event_sensor_isr(_event_sensor_instance[6]); }
  160. void ICACHE_RAM_ATTR _event_sensor_isr_13() { _event_sensor_isr(_event_sensor_instance[7]); }
  161. void ICACHE_RAM_ATTR _event_sensor_isr_14() { _event_sensor_isr(_event_sensor_instance[8]); }
  162. void ICACHE_RAM_ATTR _event_sensor_isr_15() { _event_sensor_isr(_event_sensor_instance[9]); }
  163. static void (*_event_sensor_isr_list[10])() = {
  164. _event_sensor_isr_0, _event_sensor_isr_1, _event_sensor_isr_2,
  165. _event_sensor_isr_3, _event_sensor_isr_4, _event_sensor_isr_5,
  166. _event_sensor_isr_12, _event_sensor_isr_13, _event_sensor_isr_14,
  167. _event_sensor_isr_15
  168. };
  169. void EventSensor::_attach(unsigned char gpio, unsigned char mode) {
  170. if (!gpioValid(gpio)) return;
  171. unsigned char index = gpio > 5 ? gpio-6 : gpio;
  172. if (_event_sensor_instance[index] == this) return;
  173. if (_event_sensor_instance[index]) detachInterrupt(gpio);
  174. _event_sensor_instance[index] = this;
  175. attachInterrupt(gpio, _event_sensor_isr_list[index], mode);
  176. #if SENSOR_DEBUG
  177. DEBUG_MSG_P(PSTR("[SENSOR] GPIO%d interrupt attached to %s\n"), gpio, this->description().c_str());
  178. #endif
  179. }
  180. void EventSensor::_detach(unsigned char gpio) {
  181. if (!gpioValid(gpio)) return;
  182. unsigned char index = gpio > 5 ? gpio-6 : gpio;
  183. if (_event_sensor_instance[index]) {
  184. detachInterrupt(gpio);
  185. _event_sensor_instance[index] = nullptr;
  186. #if SENSOR_DEBUG
  187. DEBUG_MSG_P(PSTR("[SENSOR] GPIO%d interrupt detached from %s\n"), gpio, _event_sensor_instance[index]->description().c_str());
  188. #endif
  189. }
  190. }
  191. #endif // SENSOR_SUPPORT && EVENTS_SUPPORT