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.

295 lines
8.7 KiB

  1. // -----------------------------------------------------------------------------
  2. // Geiger Sensor based on Event Counter Sensor
  3. // Copyright (C) 2018 by Sven Kopetzki <skopetzki at web dot de>
  4. // Documentation: https://github.com/Trickx/espurna/wiki/Geiger-counter
  5. // -----------------------------------------------------------------------------
  6. #if SENSOR_SUPPORT && GEIGER_SUPPORT
  7. #pragma once
  8. #include "BaseSensor.h"
  9. class GeigerSensor : public BaseSensor {
  10. public:
  11. // ---------------------------------------------------------------------
  12. // Public
  13. // ---------------------------------------------------------------------
  14. GeigerSensor() : BaseSensor() {
  15. _count = 2;
  16. _sensor_id = SENSOR_GEIGER_ID;
  17. }
  18. ~GeigerSensor() {
  19. _enableInterrupts(false);
  20. }
  21. // ---------------------------------------------------------------------
  22. void setGPIO(unsigned char gpio) {
  23. _gpio = gpio;
  24. }
  25. void setMode(unsigned char mode) {
  26. _mode = mode;
  27. }
  28. void setInterruptMode(unsigned char mode) {
  29. _interrupt_mode = mode;
  30. }
  31. void setDebounceTime(unsigned long debounce) {
  32. _debounce = debounce;
  33. }
  34. void setCPM2SievertFactor(unsigned int cpm2sievert) {
  35. _cpm2sievert = cpm2sievert;
  36. }
  37. // ---------------------------------------------------------------------
  38. unsigned char getGPIO() {
  39. return _gpio;
  40. }
  41. unsigned char getMode() {
  42. return _mode;
  43. }
  44. unsigned char getInterruptMode() {
  45. return _interrupt_mode;
  46. }
  47. unsigned long getDebounceTime() {
  48. return _debounce;
  49. }
  50. unsigned long getCPM2SievertFactor() {
  51. return _cpm2sievert;
  52. }
  53. // ---------------------------------------------------------------------
  54. // Sensors API
  55. // ---------------------------------------------------------------------
  56. // Initialization method, must be idempotent
  57. // Defined outside the class body
  58. void begin() {
  59. pinMode(_gpio, _mode);
  60. _enableInterrupts(true);
  61. _ready = true;
  62. }
  63. // Descriptive name of the sensor
  64. String description() {
  65. char buffer[20];
  66. snprintf(buffer, sizeof(buffer), "µSv/h @ GPIO%d", _gpio);
  67. return String(buffer);
  68. }
  69. // Descriptive name of the slot # index
  70. String description(unsigned char index) {
  71. char buffer[30];
  72. unsigned char i=0;
  73. #if GEIGER_REPORT_CPM
  74. if (index == i++) {
  75. snprintf(buffer, sizeof(buffer), "Counts per Minute @ GPIO%d", _gpio);
  76. return String(buffer);
  77. }
  78. #endif
  79. #if GEIGER_REPORT_SIEVERTS
  80. if (index == i++) {
  81. snprintf(buffer, sizeof(buffer), "CPM / %d = µSv/h", _cpm2sievert);
  82. return String(buffer);
  83. }
  84. #endif
  85. snprintf(buffer, sizeof(buffer), "Events @ GPIO%d", _gpio);
  86. return String(buffer);
  87. };
  88. // Address of the sensor (it could be the GPIO or I2C address)
  89. String address(unsigned char index) {
  90. return String(_gpio);
  91. }
  92. // Type for slot # index
  93. unsigned char type(unsigned char index) {
  94. unsigned char i=0;
  95. #if GEIGER_REPORT_CPM
  96. if (index == i++) return MAGNITUDE_GEIGER_CPM;
  97. #endif
  98. #if GEIGER_REPORT_SIEVERTS
  99. if (index == i++) return MAGNITUDE_GEIGER_SIEVERT;
  100. #endif
  101. return MAGNITUDE_NONE;
  102. }
  103. // Current value for slot # index
  104. double value(unsigned char index) {
  105. unsigned char i=0;
  106. #if GEIGER_REPORT_CPM
  107. if (index == i++) {
  108. unsigned long _period_begin = _lastreport_cpm;
  109. _lastreport_cpm = millis();
  110. double value = _events * 60000;
  111. value = value / (_lastreport_cpm-_period_begin);
  112. #if SENSOR_DEBUG
  113. char buffer[32] = {0};
  114. dtostrf(value, 1, 4, buffer);
  115. DEBUG_MSG_P(PSTR("[GEIGER] Ticks: %u | Interval: %u | CPM: %s\n"), _ticks, (_lastreport_cpm - _period_begin), buffer);
  116. #endif
  117. _events = 0;
  118. return value;
  119. }
  120. #endif
  121. #if GEIGER_REPORT_SIEVERTS
  122. if (index == i++) {
  123. unsigned long _period_begin = _lastreport_sv;
  124. _lastreport_sv = millis();
  125. double value = _ticks * 60000 / _cpm2sievert;
  126. value = value / (_lastreport_sv-_period_begin);
  127. #if SENSOR_DEBUG
  128. char buffer[32] = {0};
  129. dtostrf(value, 1, 4, buffer);
  130. DEBUG_MSG_P(PSTR("[GEIGER] Ticks: %u | Interval: %u | CPM: %s\n"), _ticks, (_lastreport_cpm - _period_begin), buffer);
  131. #endif
  132. _ticks = 0;
  133. return value;
  134. }
  135. #endif
  136. return 0;
  137. }
  138. // Handle interrupt calls
  139. void handleInterrupt(unsigned char) {
  140. static unsigned long last = 0;
  141. if (millis() - last > _debounce) {
  142. _events = _events + 1;
  143. _ticks = _ticks + 1;
  144. last = millis();
  145. }
  146. }
  147. protected:
  148. // ---------------------------------------------------------------------
  149. // Interrupt management
  150. // ---------------------------------------------------------------------
  151. void _attach(GeigerSensor * instance, unsigned char gpio, unsigned char mode);
  152. void _detach(unsigned char gpio);
  153. void _enableInterrupts(bool value) {
  154. static unsigned char _interrupt_gpio = GPIO_NONE;
  155. if (value) {
  156. if (_interrupt_gpio != GPIO_NONE) _detach(_interrupt_gpio);
  157. _attach(this, _gpio, _interrupt_mode);
  158. _interrupt_gpio = _gpio;
  159. } else if (_interrupt_gpio != GPIO_NONE) {
  160. _detach(_interrupt_gpio);
  161. _interrupt_gpio = GPIO_NONE;
  162. }
  163. }
  164. // ---------------------------------------------------------------------
  165. // Protected
  166. // ---------------------------------------------------------------------
  167. volatile unsigned long _events = 0;
  168. volatile unsigned long _ticks = 0;
  169. unsigned long _debounce = GEIGER_DEBOUNCE;
  170. unsigned int _cpm2sievert = GEIGER_CPM2SIEVERT;
  171. unsigned char _gpio;
  172. unsigned char _mode;
  173. unsigned char _interrupt_mode;
  174. // Added for µSievert calculations
  175. unsigned long _lastreport_cpm = millis();
  176. unsigned long _lastreport_sv = _lastreport_cpm;
  177. };
  178. // -----------------------------------------------------------------------------
  179. // Interrupt helpers
  180. // -----------------------------------------------------------------------------
  181. GeigerSensor * _geiger_sensor_instance[10] = {NULL};
  182. void ICACHE_RAM_ATTR _geiger_sensor_isr(unsigned char gpio) {
  183. unsigned char index = gpio > 5 ? gpio-6 : gpio;
  184. if (_geiger_sensor_instance[index]) {
  185. _geiger_sensor_instance[index]->handleInterrupt(gpio);
  186. }
  187. }
  188. void ICACHE_RAM_ATTR _geiger_sensor_isr_0() {
  189. _geiger_sensor_isr(0);
  190. }
  191. void ICACHE_RAM_ATTR _geiger_sensor_isr_1() {
  192. _geiger_sensor_isr(1);
  193. }
  194. void ICACHE_RAM_ATTR _geiger_sensor_isr_2() {
  195. _geiger_sensor_isr(2);
  196. }
  197. void ICACHE_RAM_ATTR _geiger_sensor_isr_3() {
  198. _geiger_sensor_isr(3);
  199. }
  200. void ICACHE_RAM_ATTR _geiger_sensor_isr_4() {
  201. _geiger_sensor_isr(4);
  202. }
  203. void ICACHE_RAM_ATTR _geiger_sensor_isr_5() {
  204. _geiger_sensor_isr(5);
  205. }
  206. void ICACHE_RAM_ATTR _geiger_sensor_isr_12() {
  207. _geiger_sensor_isr(12);
  208. }
  209. void ICACHE_RAM_ATTR _geiger_sensor_isr_13() {
  210. _geiger_sensor_isr(13);
  211. }
  212. void ICACHE_RAM_ATTR _geiger_sensor_isr_14() {
  213. _geiger_sensor_isr(14);
  214. }
  215. void ICACHE_RAM_ATTR _geiger_sensor_isr_15() {
  216. _geiger_sensor_isr(15);
  217. }
  218. static void (*_geiger_sensor_isr_list[10])() = {
  219. _geiger_sensor_isr_0, _geiger_sensor_isr_1, _geiger_sensor_isr_2,
  220. _geiger_sensor_isr_3, _geiger_sensor_isr_4, _geiger_sensor_isr_5,
  221. _geiger_sensor_isr_12, _geiger_sensor_isr_13, _geiger_sensor_isr_14,
  222. _geiger_sensor_isr_15
  223. };
  224. void GeigerSensor::_attach(GeigerSensor * instance, unsigned char gpio, unsigned char mode) {
  225. if (!gpioValid(gpio)) return;
  226. _detach(gpio);
  227. unsigned char index = gpio > 5 ? gpio-6 : gpio;
  228. _geiger_sensor_instance[index] = instance;
  229. attachInterrupt(gpio, _geiger_sensor_isr_list[index], mode);
  230. #if SENSOR_DEBUG
  231. DEBUG_MSG_P(PSTR("[GEIGER] GPIO%d interrupt attached to %s\n"), gpio, instance->description().c_str());
  232. #endif
  233. }
  234. void GeigerSensor::_detach(unsigned char gpio) {
  235. if (!gpioValid(gpio)) return;
  236. unsigned char index = gpio > 5 ? gpio-6 : gpio;
  237. if (_geiger_sensor_instance[index]) {
  238. detachInterrupt(gpio);
  239. #if SENSOR_DEBUG
  240. DEBUG_MSG_P(PSTR("[GEIGER] GPIO%d interrupt detached from %s\n"), gpio, _geiger_sensor_instance[index]->description().c_str());
  241. #endif
  242. _geiger_sensor_instance[index] = NULL;
  243. }
  244. }
  245. #endif // SENSOR_SUPPORT && GEIGER_SUPPORT