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.
 
 
 
 
 
 

277 lines
7.4 KiB

// -----------------------------------------------------------------------------
// Abstract sensor class (other sensor classes extend this class)
// Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
// -----------------------------------------------------------------------------
#pragma once
#include <Arduino.h>
#include "../espurna.h"
#include "../sensor.h"
#include <cstddef>
#include <cstdint>
#include <functional>
// TODO: const'ify accessors. since these are virtuals, *every* implementing class would be affected
// (...so, it would be time for a pretty big changeset...)
class BaseSensor {
public:
// Pin this to the resulting object (poor man's rtti) so we know what it can be static_cast'ed into
struct ClassKind {
ClassKind() :
_value(_last)
{
++_last;
}
int value() const {
return _value;
}
bool operator==(const ClassKind& other) const {
return _value == other._value;
}
private:
static int _last;
int _value;
};
static const ClassKind Kind;
// Generic way to pass the sensor instance to the isr
struct InterruptablePin {
InterruptablePin() = default;
~InterruptablePin() {
detach();
}
template <typename T, typename TypedCallback = void(*)(T*)>
void attach(T* instance, TypedCallback callback, int mode) {
_attach(static_cast<void*>(instance), reinterpret_cast<VoidCallback>(callback), mode);
}
InterruptablePin& operator=(unsigned char pin) {
_pin = pin;
return *this;
}
bool operator==(unsigned char other) const {
return _pin == other;
}
explicit operator String() const {
return String(_pin);
}
void detach() {
gpioUnlock(_current);
::detachInterrupt(_current);
_current = GPIO_NONE;
}
void pin(unsigned char value) {
_pin = value;
}
unsigned char pin() const {
return _pin;
}
private:
using VoidCallback = void(*)(void*);
void _attach(void* instance, VoidCallback callback, int mode) {
if (_current != _pin) {
if (!gpioLock(_pin)) {
return;
}
detach();
::attachInterruptArg(_pin, callback, instance, mode);
_current = _pin;
}
}
unsigned char _current { GPIO_NONE };
unsigned char _pin { GPIO_NONE };
};
// Generic container for magnitude types used in the sensor
struct Magnitude {
unsigned char type;
#if __cplusplus <= 201103L
constexpr Magnitude(unsigned char type) :
type(type)
{}
#endif
};
template <typename T, typename Callback>
static void findMagnitudes(const T& container, unsigned char type, Callback&& callback) {
auto begin = std::begin(container);
auto end = std::end(container);
for (auto it = begin; it != end; ++it) {
if ((*it).type == type) {
callback(std::distance(begin, it));
}
}
}
// Make sure we are correctly implementing an abstract base class
BaseSensor() = default;
virtual ~BaseSensor() = default;
// Can't copy as base, should not happen
BaseSensor(const BaseSensor&) = delete;
BaseSensor(BaseSensor&&) = delete;
BaseSensor& operator=(const BaseSensor&) = delete;
BaseSensor& operator=(BaseSensor&&) = delete;
// Initialization method, must be idempotent
virtual void begin() {
}
// Suspend / resume sensor operation
virtual void suspend() {
}
virtual void resume() {
}
// Custom hook (usually to update sensor state outside of normal methods)
virtual void notify() {
}
// Loop-like method, call it in your main loop
virtual void tick() {
}
// Pre-read hook (usually to populate registers with up-to-date data)
virtual void pre() {
}
// Post-read hook (usually to reset things)
virtual void post() {
}
// Generic calibration
virtual void calibrate() {
}
// Number of decimals for a unit (or -1 for default)
virtual signed char decimals(espurna::sensor::Unit) const {
return -1;
}
// Kind of sensor
virtual ClassKind kind() const {
return Kind;
}
// Sensor ID, must be unique
virtual unsigned char id() const = 0;
// Number of available value slots
virtual unsigned char count() const = 0;
// Descriptive name of the sensor
virtual String description() const = 0;
// Descriptive name of the slot # index
virtual String description(unsigned char) const {
return description();
}
// Address of the sensor (it could be the GPIO or I2C address)
virtual String address(unsigned char index) const = 0;
// Type for slot # index
virtual unsigned char type(unsigned char index) const = 0;
// Unit of the slot # index
virtual espurna::sensor::Unit units(unsigned char index) const {
using namespace espurna::sensor;
switch (type(index)) {
case MAGNITUDE_TEMPERATURE:
return Unit::Celcius;
case MAGNITUDE_HUMIDITY:
case MAGNITUDE_POWER_FACTOR:
return Unit::Percentage;
case MAGNITUDE_PRESSURE:
return Unit::Hectopascal;
case MAGNITUDE_CURRENT:
return Unit::Ampere;
case MAGNITUDE_VOLTAGE:
return Unit::Volt;
case MAGNITUDE_POWER_ACTIVE:
return Unit::Watt;
case MAGNITUDE_POWER_APPARENT:
return Unit::Voltampere;
case MAGNITUDE_POWER_REACTIVE:
return Unit::VoltampereReactive;
case MAGNITUDE_ENERGY_DELTA:
return Unit::Joule;
case MAGNITUDE_ENERGY:
return Unit::KilowattHour;
case MAGNITUDE_PM1DOT0:
case MAGNITUDE_PM2DOT5:
case MAGNITUDE_PM10:
case MAGNITUDE_TVOC:
case MAGNITUDE_CH2O:
return Unit::MicrogrammPerCubicMeter;
case MAGNITUDE_CO:
case MAGNITUDE_CO2:
case MAGNITUDE_NO2:
case MAGNITUDE_VOC:
return Unit::PartsPerMillion;
case MAGNITUDE_LUX:
return Unit::Lux;
case MAGNITUDE_RESISTANCE:
return Unit::Ohm;
case MAGNITUDE_HCHO:
return Unit::MilligrammPerCubicMeter;
case MAGNITUDE_GEIGER_CPM:
return Unit::CountsPerMinute;
case MAGNITUDE_GEIGER_SIEVERT:
return Unit::MicrosievertPerHour;
case MAGNITUDE_DISTANCE:
return Unit::Meter;
case MAGNITUDE_FREQUENCY:
return Unit::Hertz;
case MAGNITUDE_PH:
return Unit::Ph;
default:
break;
}
return Unit::None;
}
// Current value for slot # index
virtual double value(unsigned char index) = 0;
// Return ready status (true if ready to be read)
bool ready() const {
return _ready;
}
// Return sensor last internal error
int error() const {
return _error;
}
protected:
int _error = SENSOR_ERROR_OK;
bool _dirty = true;
bool _ready = false;
};
int BaseSensor::ClassKind::_last { 0 };
const BaseSensor::ClassKind BaseSensor::Kind;