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.

250 lines
8.9 KiB

  1. // -----------------------------------------------------------------------------
  2. // ADE7853 Sensor over I2C
  3. // Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. // Implemented by Antonio López <tonilopezmr at gmail dot com>
  5. // -----------------------------------------------------------------------------
  6. #if SENSOR_SUPPORT && ADE7953_SUPPORT
  7. #pragma once
  8. #undef I2C_SUPPORT
  9. #define I2C_SUPPORT 1 // Explicitly request I2C support.
  10. #include <Arduino.h>
  11. #include <Wire.h>
  12. #include "I2CSensor.h"
  13. #include "../utils.h"
  14. // -----------------------------------------------------------------------------
  15. // ADE7953 - Energy (Shelly 2.5)
  16. //
  17. // Based on datasheet from https://www.analog.com/en/products/ade7953.html
  18. // Based on Tasmota code https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/xnrg_07_ade7953.ino
  19. //
  20. // I2C Address: 0x38
  21. // -----------------------------------------------------------------------------
  22. #define ADE7953_PREF 1540
  23. #define ADE7953_UREF 26000
  24. #define ADE7953_IREF 10000
  25. #define ADE7953_ALL_RELAYS 0
  26. #define ADE7953_RELAY_1 1
  27. #define ADE7953_RELAY_2 2
  28. #define ADE7953_VOLTAGE 1
  29. #define ADE7953_TOTAL_DEVICES 3
  30. class ADE7953Sensor : public I2CSensor {
  31. protected:
  32. struct reading_t {
  33. float current = 0.0;
  34. float power = 0.0;
  35. float energy = 0.0;
  36. };
  37. public:
  38. // ---------------------------------------------------------------------
  39. // Public
  40. // ---------------------------------------------------------------------
  41. ADE7953Sensor(): I2CSensor() {
  42. _sensor_id = SENSOR_ADE7953_ID;
  43. _readings.resize(ADE7953_TOTAL_DEVICES);
  44. _energy_offsets.resize(ADE7953_TOTAL_DEVICES);
  45. _count = _readings.size() * ADE7953_TOTAL_DEVICES + ADE7953_VOLTAGE; //10
  46. }
  47. // ---------------------------------------------------------------------
  48. // Sensors API
  49. // ---------------------------------------------------------------------
  50. // Initialization method, must be idempotent
  51. void begin() {
  52. if (!_dirty) return;
  53. _init();
  54. _dirty = !_ready;
  55. }
  56. // Descriptive name of the sensor
  57. String description() {
  58. char buffer[25];
  59. snprintf(buffer, sizeof(buffer), "ADE7953 @ I2C (0x%02X)", _address);
  60. return String(buffer);
  61. }
  62. // Descriptive name of the slot # index
  63. String slot(unsigned char index) {
  64. return description();
  65. };
  66. // Type for slot # index
  67. unsigned char type(unsigned char index) {
  68. if (index == 0) return MAGNITUDE_VOLTAGE;
  69. index = index % ADE7953_TOTAL_DEVICES;
  70. if (index == 0) return MAGNITUDE_ENERGY;
  71. if (index == 1) return MAGNITUDE_CURRENT;
  72. if (index == 2) return MAGNITUDE_POWER_ACTIVE;
  73. return MAGNITUDE_NONE;
  74. }
  75. // Pre-read hook (usually to populate registers with up-to-date data)
  76. void pre() {
  77. uint32_t active_power1 = 0;
  78. uint32_t active_power2 = 0;
  79. uint32_t current_rms1 = 0;
  80. uint32_t current_rms2 = 0;
  81. uint32_t voltage_rms = 0;
  82. voltage_rms = read(_address, 0x31C); // Both relays
  83. current_rms1 = read(_address, 0x31B); // Relay 1
  84. if (current_rms1 < 2000) { // No load threshold (20mA)
  85. current_rms1 = 0;
  86. active_power1 = 0;
  87. } else {
  88. active_power1 = (int32_t)read(_address, 0x313) * -1; // Relay 1
  89. active_power1 = (active_power1 > 0) ? active_power1 : 0;
  90. }
  91. current_rms2 = read(_address, 0x31A); // Relay 2
  92. if (current_rms2 < 2000) { // No load threshold (20mA)
  93. current_rms2 = 0;
  94. active_power2 = 0;
  95. } else {
  96. active_power2 = (int32_t)read(_address, 0x312); // Relay 2
  97. active_power2 = (active_power2 > 0) ? active_power2 : 0;
  98. }
  99. _voltage = (float) voltage_rms / ADE7953_UREF;
  100. storeReading(
  101. ADE7953_ALL_RELAYS,
  102. (float)(current_rms1 + current_rms2) / (ADE7953_IREF * 10),
  103. (float)(active_power1 + active_power2) / (ADE7953_PREF / 10)
  104. );
  105. storeReading(
  106. ADE7953_RELAY_1,
  107. (float) current_rms1 / (ADE7953_IREF * 10),
  108. (float) active_power1 / (ADE7953_PREF / 10)
  109. );
  110. storeReading(
  111. ADE7953_RELAY_2,
  112. (float)current_rms2 / (ADE7953_IREF * 10),
  113. (float)active_power2 / (ADE7953_PREF / 10)
  114. );
  115. }
  116. inline void storeReading(unsigned int relay, float current, float power) {
  117. auto& reading_ref = _readings.at(relay);
  118. reading_ref.current = current;
  119. reading_ref.power = power;
  120. static unsigned long last = 0;
  121. if (last > 0) {
  122. reading_ref.energy += (power * (millis() - last) / 1000);
  123. }
  124. last = millis();
  125. }
  126. // Current value for slot # index
  127. double value(unsigned char index) {
  128. if (index == 0) return _voltage;
  129. int relay = (index - 1) / ADE7953_TOTAL_DEVICES;
  130. index = index % ADE7953_TOTAL_DEVICES;
  131. if (index == 0) return _energy_offsets[relay] + _readings[relay].energy;
  132. if (index == 1) return _readings[relay].current;
  133. if (index == 2) return _readings[relay].power;
  134. return 0;
  135. }
  136. unsigned int getTotalDevices() {
  137. return ADE7953_TOTAL_DEVICES;
  138. }
  139. void resetEnergy(int relay, double value = 0) {
  140. _energy_offsets[relay] = value;
  141. }
  142. protected:
  143. void _init() {
  144. nice_delay(100); // Need 100mS to init ADE7953
  145. write(_address, 0x102, 0x0004); // Locking the communication interface (Clear bit COMM_LOCK), Enable HPF
  146. write(_address, 0x0FE, 0x00AD); // Unlock register 0x120
  147. write(_address, 0x120, 0x0030); // Configure optimum setting
  148. _ready = true;
  149. }
  150. #if 0
  151. static int reg_size(uint16_t reg) {
  152. int size = 0;
  153. switch ((reg >> 8) & 0x0F) {
  154. case 0x03:
  155. size++;
  156. case 0x02:
  157. size++;
  158. case 0x01:
  159. size++;
  160. case 0x00:
  161. case 0x07:
  162. case 0x08:
  163. size++;
  164. }
  165. return size;
  166. }
  167. #else
  168. // Optimized version of the function above, -80 bytes of code
  169. // Use the known property of register addresses to calculate their size
  170. static const int reg_size(const uint16_t reg) {
  171. const uint8_t mask = ((reg >> 8) & 0b1111);
  172. if (!mask || (mask & 0b1100)) {
  173. return 1;
  174. } else if (mask & 0b0011) {
  175. return mask + 1;
  176. }
  177. return 0;
  178. }
  179. #endif
  180. void write(unsigned char address, uint16_t reg, uint32_t val) {
  181. int size = reg_size(reg);
  182. if (size) {
  183. Wire.beginTransmission(address);
  184. Wire.write((reg >> 8) & 0xFF);
  185. Wire.write(reg & 0xFF);
  186. while (size--) {
  187. Wire.write((val >> (8 * size)) & 0xFF); // Write data, MSB first
  188. }
  189. Wire.endTransmission();
  190. delayMicroseconds(5); // Bus-free time minimum 4.7us
  191. }
  192. }
  193. static uint32_t read(int address, uint16_t reg) {
  194. uint32_t response = 0;
  195. const int size = reg_size(reg);
  196. if (size) {
  197. Wire.beginTransmission(address);
  198. Wire.write((reg >> 8) & 0xFF);
  199. Wire.write(reg & 0xFF);
  200. Wire.endTransmission(0);
  201. Wire.requestFrom(address, size);
  202. if (size <= Wire.available()) {
  203. for (int i = 0; i < size; i++) {
  204. response = response << 8 | Wire.read(); // receive DATA (MSB first)
  205. }
  206. }
  207. }
  208. return response;
  209. }
  210. std::vector<reading_t> _readings;
  211. float _voltage = 0;
  212. std::vector<double> _energy_offsets;
  213. };
  214. #endif // SENSOR_SUPPORT && ADE7953_SUPPORT