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.

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