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.

165 lines
4.6 KiB

  1. // -----------------------------------------------------------------------------
  2. // MICS-2710 (and MICS-4514) NO2 Analog Sensor
  3. // Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. // -----------------------------------------------------------------------------
  5. #if SENSOR_SUPPORT && MICS2710_SUPPORT
  6. #pragma once
  7. #include "BaseAnalogSensor.h"
  8. extern "C" {
  9. #include "../libs/fs_math.h"
  10. }
  11. class MICS2710Sensor : public BaseAnalogSensor {
  12. public:
  13. // ---------------------------------------------------------------------
  14. // Public
  15. // ---------------------------------------------------------------------
  16. MICS2710Sensor() {
  17. _count = 2;
  18. _sensor_id = SENSOR_MICS2710_ID;
  19. }
  20. void calibrate() {
  21. setR0(_getResistance());
  22. }
  23. // ---------------------------------------------------------------------
  24. void setAnalogGPIO(unsigned char gpio) {
  25. _noxGPIO = gpio;
  26. }
  27. unsigned char getAnalogGPIO() {
  28. return _noxGPIO;
  29. }
  30. void setPreHeatGPIO(unsigned char gpio) {
  31. _preGPIO = gpio;
  32. }
  33. unsigned char getPreHeatGPIO() {
  34. return _preGPIO;
  35. }
  36. // ---------------------------------------------------------------------
  37. // Sensor API
  38. // ---------------------------------------------------------------------
  39. // Initialization method, must be idempotent
  40. void begin() {
  41. // Set NOX as analog input
  42. pinMode(_noxGPIO, INPUT);
  43. // Start pre-heating
  44. pinMode(_preGPIO, OUTPUT);
  45. digitalWrite(_preGPIO, HIGH);
  46. _heating = true;
  47. _start = millis();
  48. _ready = true;
  49. }
  50. // Pre-read hook (usually to populate registers with up-to-date data)
  51. void pre() {
  52. // Check pre-heat time
  53. if (_heating && (millis() - _start > MICS2710_PREHEAT_TIME)) {
  54. digitalWrite(_preGPIO, LOW);
  55. _heating = false;
  56. }
  57. if (_ready) {
  58. _Rs = _getResistance();
  59. }
  60. }
  61. // Descriptive name of the sensor
  62. String description() {
  63. return String("MICS-2710 @ TOUT");
  64. }
  65. // Descriptive name of the slot # index
  66. String description(unsigned char index) {
  67. return description();
  68. };
  69. // Address of the sensor (it could be the GPIO or I2C address)
  70. String address(unsigned char index) {
  71. return String("0");
  72. }
  73. // Type for slot # index
  74. unsigned char type(unsigned char index) {
  75. if (0 == index) return MAGNITUDE_RESISTANCE;
  76. if (1 == index) return MAGNITUDE_NO2;
  77. return MAGNITUDE_NONE;
  78. }
  79. // Current value for slot # index
  80. double value(unsigned char index) {
  81. if (0 == index) return _Rs;
  82. if (1 == index) return _getPPM();
  83. return 0;
  84. }
  85. private:
  86. unsigned long _getReading() {
  87. return analogRead(_noxGPIO);
  88. }
  89. double _getResistance() {
  90. // get voltage (1 == reference) from analog pin
  91. double voltage = (float) _getReading() / 1024.0;
  92. // schematic: 3v3 - Rs - P - Rl - GND
  93. // V(P) = 3v3 * Rl / (Rs + Rl)
  94. // Rs = 3v3 * Rl / V(P) - Rl = Rl * ( 3v3 / V(P) - 1)
  95. // 3V3 voltage is cancelled
  96. double resistance = (voltage > 0) ? _Rl * ( 1 / voltage - 1 ) : 0;
  97. return resistance;
  98. }
  99. double _getPPM() {
  100. // According to the datasheet (https://www.cdiweb.com/datasheets/e2v/mics-2710.pdf)
  101. // there is an almost linear relation between log(Rs/R0) and log(ppm).
  102. // Regression parameters have been calculated based on the graph
  103. // in the datasheet with these readings:
  104. //
  105. // Rs/R0 NO2(ppm)
  106. // 23 0.20
  107. // 42 0.30
  108. // 90 0.40
  109. // 120 0.50
  110. // 200 0.60
  111. // 410 0.90
  112. // 500 1.00
  113. // 1000 1.30
  114. // 10000 5.00
  115. return fs_pow(10, 0.5170 * fs_log10(_Rs / _R0) - 1.3954);
  116. }
  117. bool _heating = false;
  118. unsigned long _start = 0; // monitors the pre-heating time
  119. unsigned char _noxGPIO = MICS2710_PRE_PIN;
  120. unsigned char _preGPIO = MICS2710_NOX_PIN;
  121. };
  122. #endif // SENSOR_SUPPORT && MICS2710_SUPPORT