// ----------------------------------------------------------------------------- // Geiger Sensor based on Event Counter Sensor // Copyright (C) 2018 by Sven Kopetzki // Documentation: https://github.com/Trickx/espurna/wiki/Geiger-counter // ----------------------------------------------------------------------------- #if SENSOR_SUPPORT && GEIGER_SUPPORT #pragma once #include "Arduino.h" #include "BaseSensor.h" class GeigerSensor : public BaseSensor { public: // --------------------------------------------------------------------- // Public // --------------------------------------------------------------------- GeigerSensor() : BaseSensor() { _count = 2; _sensor_id = SENSOR_GEIGER_ID; } ~GeigerSensor() { _enableInterrupts(false); } // --------------------------------------------------------------------- void setGPIO(unsigned char gpio) { _gpio = gpio; } void setMode(unsigned char mode) { _mode = mode; } void setInterruptMode(unsigned char mode) { _interrupt_mode = mode; } void setDebounceTime(unsigned long debounce) { _debounce = debounce; } void setCPM2SievertFactor(unsigned int cpm2sievert) { _cpm2sievert = cpm2sievert; } // --------------------------------------------------------------------- unsigned char getGPIO() { return _gpio; } unsigned char getMode() { return _mode; } unsigned char getInterruptMode() { return _interrupt_mode; } unsigned long getDebounceTime() { return _debounce; } unsigned long getCPM2SievertFactor() { return _cpm2sievert; } // --------------------------------------------------------------------- // Sensors API // --------------------------------------------------------------------- // Initialization method, must be idempotent // Defined outside the class body void begin() { pinMode(_gpio, _mode); _enableInterrupts(true); _ready = true; } // Descriptive name of the sensor String description() { char buffer[20]; snprintf(buffer, sizeof(buffer), "µSv/h @ GPIO%d", _gpio); return String(buffer); } // Descriptive name of the slot # index String slot(unsigned char index) { char buffer[30]; unsigned char i=0; #if GEIGER_REPORT_CPM if (index == i++) { snprintf(buffer, sizeof(buffer), "Counts per Minute @ GPIO%d", _gpio); return String(buffer); } #endif #if GEIGER_REPORT_SIEVERTS if (index == i++) { snprintf(buffer, sizeof(buffer), "CPM / %d = µSv/h", _cpm2sievert); return String(buffer); } #endif snprintf(buffer, sizeof(buffer), "Events @ GPIO%d", _gpio); return String(buffer); }; // Address of the sensor (it could be the GPIO or I2C address) String address(unsigned char index) { return String(_gpio); } // Type for slot # index unsigned char type(unsigned char index) { unsigned char i=0; #if GEIGER_REPORT_CPM if (index == i++) return MAGNITUDE_GEIGER_CPM; #endif #if GEIGER_REPORT_SIEVERTS if (index == i++) return MAGNITUDE_GEIGER_SIEVERT; #endif return MAGNITUDE_NONE; } // Current value for slot # index double value(unsigned char index) { unsigned char i=0; #if GEIGER_REPORT_CPM if (index == i++) { unsigned long _period_begin = _lastreport_cpm; _lastreport_cpm = millis(); double value = _events * 60000; value = value / (_lastreport_cpm-_period_begin); #if SENSOR_DEBUG char data[128]; char buffer[10]; dtostrf(value, 1-sizeof(buffer), 4, buffer); snprintf(data, sizeof(data), "Ticks: %u | Interval: %u | CPM: %s", _ticks, (_lastreport_cpm-_period_begin), buffer); DEBUG_MSG("[GEIGER] %s\n", data); #endif _events = 0; return value; } #endif #if GEIGER_REPORT_SIEVERTS if (index == i++) { unsigned long _period_begin = _lastreport_sv; _lastreport_sv = millis(); double value = _ticks * 60000 / _cpm2sievert; value = value / (_lastreport_sv-_period_begin); #if SENSOR_DEBUG char data[128]; char buffer[10]; dtostrf(value, 1-sizeof(buffer), 4, buffer); snprintf(data, sizeof(data), "Ticks: %u | Interval: %u | µSievert: %s", _ticks, (_lastreport_sv-_period_begin), buffer); DEBUG_MSG("[GEIGER] %s\n", data); #endif _ticks = 0; return value; } #endif return 0; } // Handle interrupt calls void handleInterrupt(unsigned char gpio) { (void) gpio; static unsigned long last = 0; if (millis() - last > _debounce) { _events = _events + 1; _ticks = _ticks + 1; last = millis(); } } protected: // --------------------------------------------------------------------- // Interrupt management // --------------------------------------------------------------------- void _attach(GeigerSensor * instance, unsigned char gpio, unsigned char mode); void _detach(unsigned char gpio); void _enableInterrupts(bool value) { static unsigned char _interrupt_gpio = GPIO_NONE; if (value) { if (_interrupt_gpio != GPIO_NONE) _detach(_interrupt_gpio); _attach(this, _gpio, _interrupt_mode); _interrupt_gpio = _gpio; } else if (_interrupt_gpio != GPIO_NONE) { _detach(_interrupt_gpio); _interrupt_gpio = GPIO_NONE; } } // --------------------------------------------------------------------- // Protected // --------------------------------------------------------------------- volatile unsigned long _events = 0; volatile unsigned long _ticks = 0; unsigned long _debounce = GEIGER_DEBOUNCE; unsigned int _cpm2sievert = GEIGER_CPM2SIEVERT; unsigned char _gpio; unsigned char _mode; unsigned char _interrupt_mode; // Added for µSievert calculations unsigned long _lastreport_cpm = millis(); unsigned long _lastreport_sv = _lastreport_cpm; }; // ----------------------------------------------------------------------------- // Interrupt helpers // ----------------------------------------------------------------------------- GeigerSensor * _geiger_sensor_instance[10] = {NULL}; void ICACHE_RAM_ATTR _geiger_sensor_isr(unsigned char gpio) { unsigned char index = gpio > 5 ? gpio-6 : gpio; if (_geiger_sensor_instance[index]) { _geiger_sensor_instance[index]->handleInterrupt(gpio); } } void ICACHE_RAM_ATTR _geiger_sensor_isr_0() { _geiger_sensor_isr(0); } void ICACHE_RAM_ATTR _geiger_sensor_isr_1() { _geiger_sensor_isr(1); } void ICACHE_RAM_ATTR _geiger_sensor_isr_2() { _geiger_sensor_isr(2); } void ICACHE_RAM_ATTR _geiger_sensor_isr_3() { _geiger_sensor_isr(3); } void ICACHE_RAM_ATTR _geiger_sensor_isr_4() { _geiger_sensor_isr(4); } void ICACHE_RAM_ATTR _geiger_sensor_isr_5() { _geiger_sensor_isr(5); } void ICACHE_RAM_ATTR _geiger_sensor_isr_12() { _geiger_sensor_isr(12); } void ICACHE_RAM_ATTR _geiger_sensor_isr_13() { _geiger_sensor_isr(13); } void ICACHE_RAM_ATTR _geiger_sensor_isr_14() { _geiger_sensor_isr(14); } void ICACHE_RAM_ATTR _geiger_sensor_isr_15() { _geiger_sensor_isr(15); } static void (*_geiger_sensor_isr_list[10])() = { _geiger_sensor_isr_0, _geiger_sensor_isr_1, _geiger_sensor_isr_2, _geiger_sensor_isr_3, _geiger_sensor_isr_4, _geiger_sensor_isr_5, _geiger_sensor_isr_12, _geiger_sensor_isr_13, _geiger_sensor_isr_14, _geiger_sensor_isr_15 }; void GeigerSensor::_attach(GeigerSensor * instance, unsigned char gpio, unsigned char mode) { if (!gpioValid(gpio)) return; _detach(gpio); unsigned char index = gpio > 5 ? gpio-6 : gpio; _geiger_sensor_instance[index] = instance; attachInterrupt(gpio, _geiger_sensor_isr_list[index], mode); #if SENSOR_DEBUG DEBUG_MSG_P(PSTR("[GEIGER] GPIO%d interrupt attached to %s\n"), gpio, instance->description().c_str()); #endif } void GeigerSensor::_detach(unsigned char gpio) { if (!gpioValid(gpio)) return; unsigned char index = gpio > 5 ? gpio-6 : gpio; if (_geiger_sensor_instance[index]) { detachInterrupt(gpio); #if SENSOR_DEBUG DEBUG_MSG_P(PSTR("[GEIGER] GPIO%d interrupt detached from %s\n"), gpio, _geiger_sensor_instance[index]->description().c_str()); #endif _geiger_sensor_instance[index] = NULL; } } #endif // SENSOR_SUPPORT && GEIGER_SUPPORT