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.

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