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.

155 lines
4.0 KiB

  1. // -----------------------------------------------------------------------------
  2. // EZO™ pH Circuit from Atlas Scientific
  3. //
  4. // Copyright (C) 2018 by Rui Marinho <ruipmarinho at gmail dot com>
  5. // -----------------------------------------------------------------------------
  6. #if SENSOR_SUPPORT && EZOPH_SUPPORT
  7. #pragma once
  8. #include "BaseSensor.h"
  9. class EZOPHSensor : public BaseSensor {
  10. public:
  11. void setPort(Stream* port) {
  12. _serial = port;
  13. _dirty = true;
  14. }
  15. // ---------------------------------------------------------------------
  16. // Sensor API
  17. // ---------------------------------------------------------------------
  18. unsigned char id() const override {
  19. return SENSOR_EZOPH_ID;
  20. }
  21. unsigned char count() const override {
  22. return 1;
  23. }
  24. // Initialization method, must be idempotent
  25. void begin() override {
  26. if (!_dirty) return;
  27. _ready = true;
  28. _dirty = false;
  29. }
  30. // Descriptive name of the sensor
  31. String description() const override {
  32. return F("EZOPH");
  33. }
  34. // Address of the sensor (it could be the GPIO or I2C address)
  35. String address(unsigned char index) const override {
  36. return String(EZOPH_PORT, 10);
  37. }
  38. // Type for slot # index
  39. unsigned char type(unsigned char index) const override {
  40. if (index == 0) return MAGNITUDE_PH;
  41. return MAGNITUDE_NONE;
  42. }
  43. void tick() override {
  44. _setup();
  45. _read();
  46. }
  47. // Current value for slot # index
  48. double value(unsigned char index) override {
  49. if (index == 0) return _ph;
  50. return 0;
  51. }
  52. protected:
  53. // ---------------------------------------------------------------------
  54. // Protected
  55. // ---------------------------------------------------------------------
  56. void _setup() {
  57. if (_sync_responded) {
  58. return;
  59. }
  60. _error = SENSOR_ERROR_WARM_UP;
  61. String data;
  62. data.reserve(30);
  63. if (!_sync_requested) {
  64. _serial->write(67); // C
  65. _serial->write(44); // ,
  66. _serial->write(63); // ?
  67. _serial->write(13); // \r
  68. _serial->flush();
  69. _sync_requested = true;
  70. }
  71. data += _serial->readStringUntil('\r');
  72. if (data.startsWith("?C,")) {
  73. auto interval = TimeSource::duration(
  74. data.substring(data.indexOf(",") + 1).toInt() * 1000);
  75. if (!interval.count()) {
  76. _error = SENSOR_ERROR_OTHER;
  77. return;
  78. }
  79. _sync_interval = interval;
  80. } else if (data.startsWith("*OK")) {
  81. _sync_responded = true;
  82. }
  83. if (!_sync_responded) {
  84. return;
  85. }
  86. _error = SENSOR_ERROR_OK;
  87. }
  88. void _read() {
  89. if (_error != SENSOR_ERROR_OK) {
  90. return;
  91. }
  92. const auto now = TimeSource::now();
  93. if (now - _timestamp < _sync_interval) {
  94. return;
  95. }
  96. _timestamp = now;
  97. String data;
  98. data.reserve(30);
  99. data += _serial->readStringUntil('\r');
  100. if (data == F("*ER")) {
  101. _error = SENSOR_ERROR_OTHER;
  102. return;
  103. }
  104. _error = SENSOR_ERROR_OK;
  105. _ph = data.toFloat();
  106. }
  107. bool _sync_requested = false;
  108. bool _sync_responded = false;
  109. // Maximum continuous reading interval allowed is 99000 milliseconds.
  110. using TimeSource = espurna::time::CoreClock;
  111. TimeSource::duration _sync_interval { espurna::duration::Milliseconds(100000) };
  112. TimeSource::time_point _timestamp;
  113. double _ph = 0;
  114. Stream* _serial;
  115. };
  116. #endif // SENSOR_SUPPORT && EZOPH_SUPPORT