Mirror of espurna firmware for wireless switches and more
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.
 
 
 
 
 
 

220 lines
7.5 KiB

// -----------------------------------------------------------------------------
// Event Counter Sensor
// Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
// -----------------------------------------------------------------------------
#if SENSOR_SUPPORT && EVENTS_SUPPORT
#pragma once
#include "BaseSensor.h"
class EventSensor : public BaseSensor {
public:
static constexpr size_t SensorsMax = 8;
using TimeSource = espurna::time::CpuClock;
static constexpr unsigned char defaultPin(unsigned char index) {
return (index == 0) ? EVENTS1_PIN :
(index == 1) ? EVENTS2_PIN :
(index == 2) ? EVENTS3_PIN :
(index == 3) ? EVENTS4_PIN :
(index == 4) ? EVENTS5_PIN :
(index == 5) ? EVENTS6_PIN :
(index == 6) ? EVENTS7_PIN :
(index == 7) ? EVENTS8_PIN : GPIO_NONE;
}
static constexpr uint8_t defaultPinMode(unsigned char index) {
return (index == 0) ? EVENTS1_PIN_MODE :
(index == 1) ? EVENTS2_PIN_MODE :
(index == 2) ? EVENTS3_PIN_MODE :
(index == 3) ? EVENTS4_PIN_MODE :
(index == 4) ? EVENTS5_PIN_MODE :
(index == 5) ? EVENTS6_PIN_MODE :
(index == 6) ? EVENTS7_PIN_MODE :
(index == 7) ? EVENTS8_PIN_MODE : INPUT;
}
static constexpr espurna::duration::Milliseconds defaultDebounceTime(unsigned char index) {
return espurna::duration::Milliseconds(
(index == 0) ? EVENTS1_DEBOUNCE :
(index == 1) ? EVENTS2_DEBOUNCE :
(index == 2) ? EVENTS3_DEBOUNCE :
(index == 3) ? EVENTS4_DEBOUNCE :
(index == 4) ? EVENTS5_DEBOUNCE :
(index == 5) ? EVENTS6_DEBOUNCE :
(index == 6) ? EVENTS7_DEBOUNCE :
(index == 7) ? EVENTS8_DEBOUNCE : 50);
}
static constexpr int defaultInterruptMode(unsigned char index) {
return (index == 0) ? EVENTS1_INTERRUPT_MODE :
(index == 1) ? EVENTS2_INTERRUPT_MODE :
(index == 2) ? EVENTS3_INTERRUPT_MODE :
(index == 3) ? EVENTS4_INTERRUPT_MODE :
(index == 4) ? EVENTS5_INTERRUPT_MODE :
(index == 5) ? EVENTS6_INTERRUPT_MODE :
(index == 6) ? EVENTS7_INTERRUPT_MODE :
(index == 7) ? EVENTS8_INTERRUPT_MODE : RISING;
}
// ---------------------------------------------------------------------
void setPin(unsigned char pin) {
_pin = pin;
}
void setPinMode(unsigned char pin_mode) {
_pin_mode = pin_mode;
}
void setInterruptMode(unsigned char interrupt_mode) {
_interrupt_mode = interrupt_mode;
}
template <typename T>
void setDebounceTime(T value) {
_interrupt_debounce = std::chrono::duration_cast<TimeSource::duration>(value);
}
// ---------------------------------------------------------------------
unsigned char getPin() const {
return _pin.pin();
}
unsigned char getPinMode() const {
return _pin_mode;
}
unsigned char getInterruptMode() const {
return _interrupt_mode;
}
TimeSource::duration getDebounceTime() const {
return _interrupt_debounce;
}
// ---------------------------------------------------------------------
// Sensors API
// ---------------------------------------------------------------------
unsigned char id() const override {
return SENSOR_EVENTS_ID;
}
unsigned char count() const override {
return 2;
}
// Initialization method, must be idempotent
void begin() override {
pinMode(_pin.pin(), _pin_mode);
_enableInterrupts();
_ready = true;
}
// Descriptive name of the sensor
String description() const override {
char buffer[20];
snprintf_P(buffer, sizeof(buffer),
PSTR("INTERRUPT @ GPIO%hhu"), _pin.pin());
return String(buffer);
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char) const override {
return String(_pin.pin(), 10);
}
// Type for slot # index
unsigned char type(unsigned char index) const override {
if (index == 0) return MAGNITUDE_COUNT;
if (index == 1) return MAGNITUDE_EVENT;
return MAGNITUDE_NONE;
}
void pre() override {
_last = _current;
_current = _counter;
_difference = _current - _last;
}
double value(unsigned char index) override {
switch (index) {
case 0:
return _difference;
case 1:
return (_difference > 0) ? 1.0 : 0.0;
default:
return 0.0;
}
}
// Handle interrupt calls from isr[GPIO] functions
// No need for any locks as it cannot be nested, esp8266/Arduino Core already masks all GPIO handlers before calling this function
static void IRAM_ATTR handleDebouncedInterrupt(EventSensor* instance) {
instance->debouncedInterrupt();
}
static void IRAM_ATTR handleInterrupt(EventSensor* instance) {
++(instance->_counter);
}
protected:
// ---------------------------------------------------------------------
// Interrupt management
// ---------------------------------------------------------------------
void IRAM_ATTR debouncedInterrupt() {
// Debounce is based around ccount (32bit value), overflowing every:
// ~53s when F_CPU is 80MHz
// ~26s when F_CPU is 160MHz
// see: cores/esp8266/Arduino.h definitions
//
// To convert to / from normal time values, use:
// - microsecondsToClockCycles(microseconds)
// - clockCyclesToMicroseconds(cycles)
// Since the division operation on this chip is pretty slow,
// avoid doing the conversion here and instead do that at initialization
const auto now = TimeSource::now();
if (now - _interrupt_last > _interrupt_debounce) {
_interrupt_last = now;
++_counter;
}
}
void _enableInterrupts() {
if (_interrupt_debounce.count()) {
_interrupt_last = TimeSource::now();
_pin.attach(this, handleDebouncedInterrupt, _interrupt_mode);
} else {
_pin.attach(this, handleInterrupt, _interrupt_mode);
}
}
// ---------------------------------------------------------------------
// Protected
// ---------------------------------------------------------------------
unsigned long _counter { 0ul };
unsigned long _current { 0ul };
unsigned long _last { 0ul };
unsigned long _difference { 0ul };
TimeSource::duration _interrupt_debounce;
TimeSource::time_point _interrupt_last;
InterruptablePin _pin{};
uint8_t _pin_mode { INPUT };
int _interrupt_mode { RISING };
};
#endif // SENSOR_SUPPORT && EVENTS_SUPPORT