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.

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