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

  1. // -----------------------------------------------------------------------------
  2. // Abstract sensor class (other sensor classes extend this class)
  3. // Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. // -----------------------------------------------------------------------------
  5. #pragma once
  6. #include <Arduino.h>
  7. #include "../espurna.h"
  8. #include "../sensor.h"
  9. #include <cstddef>
  10. #include <cstdint>
  11. #include <functional>
  12. // TODO: const'ify accessors. since these are virtuals, *every* implementing class would be affected
  13. // (...so, it would be time for a pretty big changeset...)
  14. class BaseSensor {
  15. public:
  16. // Pin this to the resulting object (poor man's rtti) so we know what it can be static_cast'ed into
  17. struct ClassKind {
  18. ClassKind() :
  19. _value(_last)
  20. {
  21. ++_last;
  22. }
  23. int value() const {
  24. return _value;
  25. }
  26. bool operator==(const ClassKind& other) const {
  27. return _value == other._value;
  28. }
  29. private:
  30. static int _last;
  31. int _value;
  32. };
  33. static const ClassKind Kind;
  34. // Generic way to pass the sensor instance to the isr
  35. struct InterruptablePin {
  36. InterruptablePin() = default;
  37. ~InterruptablePin() {
  38. detach();
  39. }
  40. template <typename T, typename TypedCallback = void(*)(T*)>
  41. void attach(T* instance, TypedCallback callback, int mode) {
  42. _attach(static_cast<void*>(instance), reinterpret_cast<VoidCallback>(callback), mode);
  43. }
  44. InterruptablePin& operator=(unsigned char pin) {
  45. _pin = pin;
  46. return *this;
  47. }
  48. bool operator==(unsigned char other) const {
  49. return _pin == other;
  50. }
  51. explicit operator String() const {
  52. return String(_pin);
  53. }
  54. void detach() {
  55. gpioUnlock(_current);
  56. ::detachInterrupt(_current);
  57. _current = GPIO_NONE;
  58. }
  59. void pin(unsigned char value) {
  60. _pin = value;
  61. }
  62. unsigned char pin() const {
  63. return _pin;
  64. }
  65. private:
  66. using VoidCallback = void(*)(void*);
  67. void _attach(void* instance, VoidCallback callback, int mode) {
  68. if (_current != _pin) {
  69. if (!gpioLock(_pin)) {
  70. return;
  71. }
  72. detach();
  73. ::attachInterruptArg(_pin, callback, instance, mode);
  74. _current = _pin;
  75. }
  76. }
  77. unsigned char _current { GPIO_NONE };
  78. unsigned char _pin { GPIO_NONE };
  79. };
  80. // Generic container for magnitude types used in the sensor
  81. struct Magnitude {
  82. unsigned char type;
  83. #if __cplusplus <= 201103L
  84. constexpr Magnitude(unsigned char type) :
  85. type(type)
  86. {}
  87. #endif
  88. };
  89. template <typename T, typename Callback>
  90. static void findMagnitudes(const T& container, unsigned char type, Callback&& callback) {
  91. auto begin = std::begin(container);
  92. auto end = std::end(container);
  93. for (auto it = begin; it != end; ++it) {
  94. if ((*it).type == type) {
  95. callback(std::distance(begin, it));
  96. }
  97. }
  98. }
  99. // Make sure we are correctly implementing an abstract base class
  100. BaseSensor() = default;
  101. virtual ~BaseSensor() = default;
  102. // Can't copy as base, should not happen
  103. BaseSensor(const BaseSensor&) = delete;
  104. BaseSensor(BaseSensor&&) = delete;
  105. BaseSensor& operator=(const BaseSensor&) = delete;
  106. BaseSensor& operator=(BaseSensor&&) = delete;
  107. // Initialization method, must be idempotent
  108. virtual void begin() {
  109. }
  110. // Suspend / resume sensor operation
  111. virtual void suspend() {
  112. }
  113. virtual void resume() {
  114. }
  115. // Custom hook (usually to update sensor state outside of normal methods)
  116. virtual void notify() {
  117. }
  118. // Loop-like method, call it in your main loop
  119. virtual void tick() {
  120. }
  121. // Pre-read hook (usually to populate registers with up-to-date data)
  122. virtual void pre() {
  123. }
  124. // Post-read hook (usually to reset things)
  125. virtual void post() {
  126. }
  127. // Generic calibration
  128. virtual void calibrate() {
  129. }
  130. // Number of decimals for a unit (or -1 for default)
  131. virtual signed char decimals(espurna::sensor::Unit) const {
  132. return -1;
  133. }
  134. // Kind of sensor
  135. virtual ClassKind kind() const {
  136. return Kind;
  137. }
  138. // Sensor ID, must be unique
  139. virtual unsigned char id() const = 0;
  140. // Number of available value slots
  141. virtual unsigned char count() const = 0;
  142. // Descriptive name of the sensor
  143. virtual String description() const = 0;
  144. // Descriptive name of the slot # index
  145. virtual String description(unsigned char) const {
  146. return description();
  147. }
  148. // Address of the sensor (it could be the GPIO or I2C address)
  149. virtual String address(unsigned char index) const = 0;
  150. // Type for slot # index
  151. virtual unsigned char type(unsigned char index) const = 0;
  152. // Unit of the slot # index
  153. virtual espurna::sensor::Unit units(unsigned char index) const {
  154. using namespace espurna::sensor;
  155. switch (type(index)) {
  156. case MAGNITUDE_TEMPERATURE:
  157. return Unit::Celcius;
  158. case MAGNITUDE_HUMIDITY:
  159. case MAGNITUDE_POWER_FACTOR:
  160. return Unit::Percentage;
  161. case MAGNITUDE_PRESSURE:
  162. return Unit::Hectopascal;
  163. case MAGNITUDE_CURRENT:
  164. return Unit::Ampere;
  165. case MAGNITUDE_VOLTAGE:
  166. return Unit::Volt;
  167. case MAGNITUDE_POWER_ACTIVE:
  168. return Unit::Watt;
  169. case MAGNITUDE_POWER_APPARENT:
  170. return Unit::Voltampere;
  171. case MAGNITUDE_POWER_REACTIVE:
  172. return Unit::VoltampereReactive;
  173. case MAGNITUDE_ENERGY_DELTA:
  174. return Unit::Joule;
  175. case MAGNITUDE_ENERGY:
  176. return Unit::KilowattHour;
  177. case MAGNITUDE_PM1DOT0:
  178. case MAGNITUDE_PM2DOT5:
  179. case MAGNITUDE_PM10:
  180. case MAGNITUDE_TVOC:
  181. case MAGNITUDE_CH2O:
  182. return Unit::MicrogrammPerCubicMeter;
  183. case MAGNITUDE_CO:
  184. case MAGNITUDE_CO2:
  185. case MAGNITUDE_NO2:
  186. case MAGNITUDE_VOC:
  187. return Unit::PartsPerMillion;
  188. case MAGNITUDE_LUX:
  189. return Unit::Lux;
  190. case MAGNITUDE_RESISTANCE:
  191. return Unit::Ohm;
  192. case MAGNITUDE_HCHO:
  193. return Unit::MilligrammPerCubicMeter;
  194. case MAGNITUDE_GEIGER_CPM:
  195. return Unit::CountsPerMinute;
  196. case MAGNITUDE_GEIGER_SIEVERT:
  197. return Unit::MicrosievertPerHour;
  198. case MAGNITUDE_DISTANCE:
  199. return Unit::Meter;
  200. case MAGNITUDE_FREQUENCY:
  201. return Unit::Hertz;
  202. case MAGNITUDE_PH:
  203. return Unit::Ph;
  204. default:
  205. break;
  206. }
  207. return Unit::None;
  208. }
  209. // Current value for slot # index
  210. virtual double value(unsigned char index) = 0;
  211. // Return ready status (true if ready to be read)
  212. bool ready() const {
  213. return _ready;
  214. }
  215. // Return sensor last internal error
  216. int error() const {
  217. return _error;
  218. }
  219. protected:
  220. int _error = SENSOR_ERROR_OK;
  221. bool _dirty = true;
  222. bool _ready = false;
  223. };
  224. int BaseSensor::ClassKind::_last { 0 };
  225. const BaseSensor::ClassKind BaseSensor::Kind;