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.

121 lines
3.8 KiB

  1. // -----------------------------------------------------------------------------
  2. // Eergy monitor sensor
  3. // -----------------------------------------------------------------------------
  4. #pragma once
  5. #include "Arduino.h"
  6. #include "BaseSensor.h"
  7. #define EMON_DEBUG 0
  8. class EmonSensor : public BaseSensor {
  9. public:
  10. EmonSensor(double voltage, unsigned char bits, double ref, double ratio): BaseSensor() {
  11. // Cache
  12. _voltage = voltage;
  13. _adc_counts = 1 << bits;
  14. _pivot = _adc_counts >> 1;
  15. // Calculate factor
  16. _current_factor = ratio * ref / _adc_counts;
  17. // Calculate multiplier
  18. calculateMultiplier();
  19. #if EMON_DEBUG
  20. Serial.print("[EMON] Current ratio: "); Serial.println(ratio);
  21. Serial.print("[EMON] Ref. Voltage: "); Serial.println(_voltage);
  22. Serial.print("[EMON] ADC Counts: "); Serial.println(_adc_counts);
  23. Serial.print("[EMON] Current factor: "); Serial.println(_current_factor);
  24. Serial.print("[EMON] Multiplier: "); Serial.println(_multiplier);
  25. #endif
  26. }
  27. protected:
  28. virtual unsigned int readADC(unsigned char port) {}
  29. void calculateMultiplier() {
  30. unsigned int s = 1;
  31. unsigned int i = 1;
  32. unsigned int m = s * i;
  33. while (m * _current_factor < 1) {
  34. _multiplier = m;
  35. i = (i == 1) ? 2 : (i == 2) ? 5 : 1;
  36. if (i == 1) s *= 10;
  37. m = s * i;
  38. }
  39. }
  40. double read(unsigned char port) {
  41. int sample;
  42. int max = 0;
  43. int min = _adc_counts;
  44. double filtered;
  45. double sum = 0;
  46. unsigned long time_span = millis();
  47. for (unsigned long i=0; i<_samples; i++) {
  48. // Read analog value
  49. sample = readADC(port);
  50. if (sample > max) max = sample;
  51. if (sample < min) min = sample;
  52. // Digital low pass filter extracts the VDC offset
  53. _pivot = (_pivot + (sample - _pivot) / _adc_counts);
  54. filtered = sample - _pivot;
  55. // Root-mean-square method
  56. sum += (filtered * filtered);
  57. }
  58. time_span = millis() - time_span;
  59. // Quick fix
  60. if (_pivot < min || max < _pivot) {
  61. _pivot = (max + min) / 2.0;
  62. }
  63. // Calculate current
  64. double rms = _samples > 0 ? sqrt(sum / _samples) : 0;
  65. double current = _current_factor * rms;
  66. current = (double) (int(current * _multiplier) - 1) / _multiplier;
  67. if (current < 0) current = 0;
  68. #if EMON_DEBUG
  69. Serial.print("[EMON] Total samples: "); Serial.println(_samples);
  70. Serial.print("[EMON] Total time (ms): "); Serial.println(time_span);
  71. Serial.print("[EMON] Sample frequency (Hz): "); Serial.println(1000 * _samples / time_span);
  72. Serial.print("[EMON] Max value: "); Serial.println(max);
  73. Serial.print("[EMON] Min value: "); Serial.println(min);
  74. Serial.print("[EMON] Midpoint value: "); Serial.println(_pivot);
  75. Serial.print("[EMON] RMS value: "); Serial.println(rms);
  76. Serial.print("[EMON] Current: "); Serial.println(current);
  77. #endif
  78. // Check timing
  79. if (time_span > EMON_MAX_TIME) {
  80. _samples = (_samples * EMON_MAX_TIME) / time_span;
  81. }
  82. return current;
  83. }
  84. double _voltage;
  85. unsigned int _adc_counts;
  86. unsigned int _multiplier = 1;
  87. double _current_factor;
  88. double _pivot;
  89. unsigned long _samples = EMON_MAX_SAMPLES;
  90. };