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.

156 lines
4.3 KiB

  1. // -----------------------------------------------------------------------------
  2. // Eergy monitor sensor
  3. // -----------------------------------------------------------------------------
  4. #pragma once
  5. #include "Arduino.h"
  6. #include "BaseSensor.h"
  7. class AnalogEmonSensor : public BaseSensor {
  8. public:
  9. AnalogEmonSensor(unsigned char gpio, double voltage, unsigned char bits, double ref, double ratio): BaseSensor() {
  10. // Prepare GPIO
  11. pinMode(gpio, INPUT);
  12. // Cache
  13. _gpio = gpio;
  14. _voltage = voltage;
  15. _adc_counts = 1 << bits;
  16. _pivot = _adc_counts >> 1;
  17. _count = 2;
  18. // Calculate factor
  19. _current_factor = ratio * ref / _adc_counts;
  20. // Calculate multiplier
  21. calculateMultiplier();
  22. // warmup
  23. read(EMON_ANALOG_WARMUP_VALUE, EMON_ANALOG_WARMUP_MODE, _gpio);
  24. }
  25. // Descriptive name of the sensor
  26. String name() {
  27. char buffer[20];
  28. snprintf(buffer, sizeof(buffer), "ANALOG EMON @ GPIO%d", _gpio);
  29. return String(buffer);
  30. }
  31. // Descriptive name of the slot # index
  32. String slot(unsigned char index) {
  33. return name();
  34. }
  35. // Type for slot # index
  36. magnitude_t type(unsigned char index) {
  37. _error = SENSOR_ERROR_OK;
  38. if (index == 0) return MAGNITUDE_CURRENT;
  39. if (index == 1) return MAGNITUDE_POWER_APPARENT;
  40. _error = SENSOR_ERROR_OUT_OF_RANGE;
  41. return MAGNITUDE_NONE;
  42. }
  43. // Current value for slot # index
  44. double value(unsigned char index) {
  45. _error = SENSOR_ERROR_OK;
  46. // Cache the value
  47. static unsigned long last = 0;
  48. static double current = 0;
  49. if ((last == 0) || (millis() - last > 1000)) {
  50. current = read(EMON_ANALOG_READ_VALUE, EMON_ANALOG_READ_MODE, _gpio);
  51. last = millis();
  52. }
  53. if (index == 0) return current;
  54. if (index == 1) return current * _voltage;
  55. _error = SENSOR_ERROR_OUT_OF_RANGE;
  56. return 0;
  57. }
  58. protected:
  59. unsigned int readADC(unsigned char port) {
  60. return analogRead(port);
  61. }
  62. void calculateMultiplier() {
  63. unsigned int s = 1;
  64. unsigned int i = 1;
  65. unsigned int m = s * i;
  66. while (m * _current_factor < 1) {
  67. _multiplier = m;
  68. i = (i == 1) ? 2 : (i == 2) ? 5 : 1;
  69. if (i == 1) s *= 10;
  70. m = s * i;
  71. }
  72. }
  73. double read(unsigned long value, unsigned char mode, unsigned char port) {
  74. int sample;
  75. int max = 0;
  76. int min = _adc_counts;
  77. double filtered;
  78. double sum = 0;
  79. unsigned long start = millis();
  80. unsigned long samples = 0;
  81. while (true) {
  82. // Read analog value
  83. sample = readADC(port);
  84. if (sample > max) max = sample;
  85. if (sample < min) min = sample;
  86. // Digital low pass filter extracts the VDC offset
  87. _pivot = (_pivot + (sample - _pivot) / EMON_ANALOG_FILTER_SPEED);
  88. filtered = sample - _pivot;
  89. // Root-mean-square method
  90. sum += (filtered * filtered);
  91. ++samples;
  92. // Exit condition
  93. if (mode == EMON_ANALOG_MODE_SAMPLES) {
  94. if (samples >= value) break;
  95. } else {
  96. if (millis() - start >= value) break;
  97. }
  98. yield();
  99. }
  100. // Quick fix
  101. if (_pivot < min || max < _pivot) {
  102. _pivot = (max + min) / 2.0;
  103. }
  104. double rms = samples > 0 ? sqrt(sum / samples) : 0;
  105. double current = _current_factor * rms;
  106. current = (double) (round(current * _multiplier) - 1) / _multiplier;
  107. if (current < 0) current = 0;
  108. return current;
  109. }
  110. double _voltage;
  111. unsigned char _gpio;
  112. unsigned int _adc_counts;
  113. unsigned int _multiplier = 1;
  114. double _current_factor;
  115. double _pivot;
  116. };