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.

992 lines
31 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. /*
  2. SENSOR MODULE
  3. Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if SENSOR_SUPPORT
  6. #include <vector>
  7. #include "filters/MaxFilter.h"
  8. #include "filters/MedianFilter.h"
  9. #include "filters/MovingAverageFilter.h"
  10. #include "sensors/BaseSensor.h"
  11. typedef struct {
  12. BaseSensor * sensor; // Sensor object
  13. BaseFilter * filter; // Filter object
  14. unsigned char local; // Local index in its provider
  15. unsigned char type; // Type of measurement
  16. unsigned char global; // Global index in its type
  17. double current; // Current (last) value, unfiltered
  18. double filtered; // Filtered (averaged) value
  19. double reported; // Last reported value
  20. double min_change; // Minimum value change to report
  21. } sensor_magnitude_t;
  22. std::vector<BaseSensor *> _sensors;
  23. std::vector<sensor_magnitude_t> _magnitudes;
  24. bool _sensors_ready = false;
  25. unsigned char _counts[MAGNITUDE_MAX];
  26. bool _sensor_realtime = API_REAL_TIME_VALUES;
  27. unsigned long _sensor_read_interval = 1000 * SENSOR_READ_INTERVAL;
  28. unsigned char _sensor_report_every = SENSOR_REPORT_EVERY;
  29. unsigned char _sensor_power_units = SENSOR_POWER_UNITS;
  30. unsigned char _sensor_energy_units = SENSOR_ENERGY_UNITS;
  31. unsigned char _sensor_temperature_units = SENSOR_TEMPERATURE_UNITS;
  32. double _sensor_temperature_correction = SENSOR_TEMPERATURE_CORRECTION;
  33. double _sensor_humidity_correction = SENSOR_HUMIDITY_CORRECTION;
  34. // -----------------------------------------------------------------------------
  35. // Private
  36. // -----------------------------------------------------------------------------
  37. unsigned char _magnitudeDecimals(unsigned char type) {
  38. // Hardcoded decimals (these should be linked to the unit, instead of the magnitude)
  39. if (type == MAGNITUDE_ENERGY ||
  40. type == MAGNITUDE_ENERGY_DELTA) {
  41. if (_sensor_energy_units == ENERGY_KWH) return 3;
  42. }
  43. if (type == MAGNITUDE_POWER_ACTIVE ||
  44. type == MAGNITUDE_POWER_APPARENT ||
  45. type == MAGNITUDE_POWER_REACTIVE) {
  46. if (_sensor_power_units == POWER_KILOWATTS) return 3;
  47. }
  48. if (type < MAGNITUDE_MAX) return pgm_read_byte(magnitude_decimals + type);
  49. return 0;
  50. }
  51. double _magnitudeProcess(unsigned char type, double value) {
  52. // Hardcoded conversions (these should be linked to the unit, instead of the magnitude)
  53. if (type == MAGNITUDE_TEMPERATURE) {
  54. if (_sensor_temperature_units == TMP_FAHRENHEIT) value = value * 1.8 + 32;
  55. value = value + _sensor_temperature_correction;
  56. }
  57. if (type == MAGNITUDE_HUMIDITY) {
  58. value = constrain(value + _sensor_humidity_correction, 0, 100);
  59. }
  60. if (type == MAGNITUDE_ENERGY ||
  61. type == MAGNITUDE_ENERGY_DELTA) {
  62. if (_sensor_energy_units == ENERGY_KWH) value = value / 3600000;
  63. }
  64. if (type == MAGNITUDE_POWER_ACTIVE ||
  65. type == MAGNITUDE_POWER_APPARENT ||
  66. type == MAGNITUDE_POWER_REACTIVE) {
  67. if (_sensor_power_units == POWER_KILOWATTS) value = value / 1000;
  68. }
  69. return roundTo(value, _magnitudeDecimals(type));
  70. }
  71. // -----------------------------------------------------------------------------
  72. #if WEB_SUPPORT
  73. bool _sensorWebSocketOnReceive(const char * key, JsonVariant& value) {
  74. if (strncmp(key, "pwr", 3) == 0) return true;
  75. if (strncmp(key, "sns", 3) == 0) return true;
  76. if (strncmp(key, "tmp", 3) == 0) return true;
  77. if (strncmp(key, "hum", 3) == 0) return true;
  78. if (strncmp(key, "energy", 6) == 0) return true;
  79. return false;
  80. }
  81. void _sensorWebSocketSendData(JsonObject& root) {
  82. char buffer[10];
  83. bool hasTemperature = false;
  84. bool hasHumidity = false;
  85. JsonArray& list = root.createNestedArray("magnitudes");
  86. for (unsigned char i=0; i<_magnitudes.size(); i++) {
  87. sensor_magnitude_t magnitude = _magnitudes[i];
  88. unsigned char decimals = _magnitudeDecimals(magnitude.type);
  89. dtostrf(magnitude.current, 1-sizeof(buffer), decimals, buffer);
  90. JsonObject& element = list.createNestedObject();
  91. element["index"] = int(magnitude.global);
  92. element["type"] = int(magnitude.type);
  93. element["value"] = String(buffer);
  94. element["units"] = magnitudeUnits(magnitude.type);
  95. element["description"] = magnitude.sensor->slot(magnitude.local);
  96. element["error"] = magnitude.sensor->error();
  97. if (magnitude.type == MAGNITUDE_TEMPERATURE) hasTemperature = true;
  98. if (magnitude.type == MAGNITUDE_HUMIDITY) hasHumidity = true;
  99. }
  100. if (hasTemperature) root["temperatureVisible"] = 1;
  101. if (hasHumidity) root["humidityVisible"] = 1;
  102. }
  103. void _sensorWebSocketStart(JsonObject& root) {
  104. for (unsigned char i=0; i<_sensors.size(); i++) {
  105. BaseSensor * sensor = _sensors[i];
  106. #if EMON_ANALOG_SUPPORT
  107. if (sensor->getID() == SENSOR_EMON_ANALOG_ID) {
  108. root["emonVisible"] = 1;
  109. root["pwrVisible"] = 1;
  110. root["pwrVoltage"] = ((EmonAnalogSensor *) sensor)->getVoltage();
  111. }
  112. #endif
  113. #if HLW8012_SUPPORT
  114. if (sensor->getID() == SENSOR_HLW8012_ID) {
  115. root["hlwVisible"] = 1;
  116. root["pwrVisible"] = 1;
  117. }
  118. #endif
  119. #if V9261F_SUPPORT
  120. if (sensor->getID() == SENSOR_V9261F_ID) {
  121. root["pwrVisible"] = 1;
  122. }
  123. #endif
  124. #if ECH1560_SUPPORT
  125. if (sensor->getID() == SENSOR_ECH1560_ID) {
  126. root["pwrVisible"] = 1;
  127. }
  128. #endif
  129. #if PZEM004T_SUPPORT
  130. if (sensor->getID() == SENSOR_PZEM004T_ID) {
  131. root["pwrVisible"] = 1;
  132. }
  133. #endif
  134. }
  135. if (_magnitudes.size() > 0) {
  136. root["sensorsVisible"] = 1;
  137. //root["apiRealTime"] = _sensor_realtime;
  138. root["pwrUnits"] = _sensor_power_units;
  139. root["energyUnits"] = _sensor_energy_units;
  140. root["tmpUnits"] = _sensor_temperature_units;
  141. root["tmpCorrection"] = _sensor_temperature_correction;
  142. root["humCorrection"] = _sensor_humidity_correction;
  143. root["snsRead"] = _sensor_read_interval / 1000;
  144. root["snsReport"] = _sensor_report_every;
  145. }
  146. /*
  147. // Sensors manifest
  148. JsonArray& manifest = root.createNestedArray("manifest");
  149. #if BMX280_SUPPORT
  150. BMX280Sensor::manifest(manifest);
  151. #endif
  152. // Sensors configuration
  153. JsonArray& sensors = root.createNestedArray("sensors");
  154. for (unsigned char i; i<_sensors.size(); i++) {
  155. JsonObject& sensor = sensors.createNestedObject();
  156. sensor["index"] = i;
  157. sensor["id"] = _sensors[i]->getID();
  158. _sensors[i]->getConfig(sensor);
  159. }
  160. */
  161. }
  162. void _sensorAPISetup() {
  163. for (unsigned char magnitude_id=0; magnitude_id<_magnitudes.size(); magnitude_id++) {
  164. sensor_magnitude_t magnitude = _magnitudes[magnitude_id];
  165. String topic = magnitudeTopic(magnitude.type);
  166. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) topic = topic + "/" + String(magnitude.global);
  167. apiRegister(topic.c_str(), [magnitude_id](char * buffer, size_t len) {
  168. sensor_magnitude_t magnitude = _magnitudes[magnitude_id];
  169. unsigned char decimals = _magnitudeDecimals(magnitude.type);
  170. double value = _sensor_realtime ? magnitude.current : magnitude.filtered;
  171. dtostrf(value, 1-len, decimals, buffer);
  172. });
  173. }
  174. }
  175. #endif
  176. #if TERMINAL_SUPPORT
  177. void _sensorInitCommands() {
  178. settingsRegisterCommand(F("MAGNITUDES"), [](Embedis* e) {
  179. for (unsigned char i=0; i<_magnitudes.size(); i++) {
  180. sensor_magnitude_t magnitude = _magnitudes[i];
  181. DEBUG_MSG_P(PSTR("[SENSOR] * %2d: %s @ %s (%s/%d)\n"),
  182. i,
  183. magnitudeTopic(magnitude.type).c_str(),
  184. magnitude.sensor->slot(magnitude.local).c_str(),
  185. magnitudeTopic(magnitude.type).c_str(),
  186. magnitude.global
  187. );
  188. }
  189. DEBUG_MSG_P(PSTR("+OK\n"));
  190. });
  191. }
  192. #endif
  193. void _sensorTick() {
  194. for (unsigned char i=0; i<_sensors.size(); i++) {
  195. _sensors[i]->tick();
  196. }
  197. }
  198. void _sensorPre() {
  199. for (unsigned char i=0; i<_sensors.size(); i++) {
  200. _sensors[i]->pre();
  201. if (!_sensors[i]->status()) {
  202. DEBUG_MSG_P(PSTR("[SENSOR] Error reading data from %s (error: %d)\n"),
  203. _sensors[i]->description().c_str(),
  204. _sensors[i]->error()
  205. );
  206. }
  207. }
  208. }
  209. void _sensorPost() {
  210. for (unsigned char i=0; i<_sensors.size(); i++) {
  211. _sensors[i]->post();
  212. }
  213. }
  214. // -----------------------------------------------------------------------------
  215. // Sensor initialization
  216. // -----------------------------------------------------------------------------
  217. void _sensorLoad() {
  218. /*
  219. This is temporal, in the future sensors will be initialized based on
  220. soft configuration (data stored in EEPROM config) so you will be able
  221. to define and configure new sensors on the fly
  222. At the time being, only enabled sensors (those with *_SUPPORT to 1) are being
  223. loaded and initialized here. If you want to add new sensors of the same type
  224. just duplicate the block and change the arguments for the set* methods.
  225. Check the DHT block below for an example
  226. */
  227. #if ANALOG_SUPPORT
  228. {
  229. AnalogSensor * sensor = new AnalogSensor();
  230. _sensors.push_back(sensor);
  231. }
  232. #endif
  233. #if BH1750_SUPPORT
  234. {
  235. BH1750Sensor * sensor = new BH1750Sensor();
  236. sensor->setAddress(BH1750_ADDRESS);
  237. sensor->setMode(BH1750_MODE);
  238. _sensors.push_back(sensor);
  239. }
  240. #endif
  241. #if BMX280_SUPPORT
  242. {
  243. BMX280Sensor * sensor = new BMX280Sensor();
  244. sensor->setAddress(BMX280_ADDRESS);
  245. _sensors.push_back(sensor);
  246. }
  247. #endif
  248. #if DALLAS_SUPPORT
  249. {
  250. DallasSensor * sensor = new DallasSensor();
  251. sensor->setGPIO(DALLAS_PIN);
  252. _sensors.push_back(sensor);
  253. }
  254. #endif
  255. #if DHT_SUPPORT
  256. {
  257. DHTSensor * sensor = new DHTSensor();
  258. sensor->setGPIO(DHT_PIN);
  259. sensor->setType(DHT_TYPE);
  260. _sensors.push_back(sensor);
  261. }
  262. #endif
  263. /*
  264. // Example on how to add a second DHT sensor
  265. // DHT2_PIN and DHT2_TYPE should be defined in sensors.h file
  266. #if DHT_SUPPORT
  267. {
  268. DHTSensor * sensor = new DHTSensor();
  269. sensor->setGPIO(DHT2_PIN);
  270. sensor->setType(DHT2_TYPE);
  271. _sensors.push_back(sensor);
  272. }
  273. #endif
  274. */
  275. #if DIGITAL_SUPPORT
  276. {
  277. DigitalSensor * sensor = new DigitalSensor();
  278. sensor->setGPIO(DIGITAL_PIN);
  279. sensor->setMode(DIGITAL_PIN_MODE);
  280. sensor->setDefault(DIGITAL_DEFAULT_STATE);
  281. _sensors.push_back(sensor);
  282. }
  283. #endif
  284. #if ECH1560_SUPPORT
  285. {
  286. ECH1560Sensor * sensor = new ECH1560Sensor();
  287. sensor->setCLK(ECH1560_CLK_PIN);
  288. sensor->setMISO(ECH1560_MISO_PIN);
  289. sensor->setInverted(ECH1560_INVERTED);
  290. _sensors.push_back(sensor);
  291. }
  292. #endif
  293. #if EMON_ADC121_SUPPORT
  294. {
  295. EmonADC121Sensor * sensor = new EmonADC121Sensor();
  296. sensor->setAddress(EMON_ADC121_I2C_ADDRESS);
  297. sensor->setVoltage(EMON_MAINS_VOLTAGE);
  298. sensor->setReference(EMON_REFERENCE_VOLTAGE);
  299. sensor->setCurrentRatio(0, EMON_CURRENT_RATIO);
  300. _sensors.push_back(sensor);
  301. }
  302. #endif
  303. #if EMON_ADS1X15_SUPPORT
  304. {
  305. EmonADS1X15Sensor * sensor = new EmonADS1X15Sensor();
  306. sensor->setAddress(EMON_ADS1X15_I2C_ADDRESS);
  307. sensor->setType(EMON_ADS1X15_TYPE);
  308. sensor->setMask(EMON_ADS1X15_MASK);
  309. sensor->setGain(EMON_ADS1X15_GAIN);
  310. sensor->setVoltage(EMON_MAINS_VOLTAGE);
  311. sensor->setCurrentRatio(0, EMON_CURRENT_RATIO);
  312. sensor->setCurrentRatio(1, EMON_CURRENT_RATIO);
  313. sensor->setCurrentRatio(2, EMON_CURRENT_RATIO);
  314. sensor->setCurrentRatio(3, EMON_CURRENT_RATIO);
  315. _sensors.push_back(sensor);
  316. }
  317. #endif
  318. #if EMON_ANALOG_SUPPORT
  319. {
  320. EmonAnalogSensor * sensor = new EmonAnalogSensor();
  321. sensor->setVoltage(EMON_MAINS_VOLTAGE);
  322. sensor->setReference(EMON_REFERENCE_VOLTAGE);
  323. sensor->setCurrentRatio(0, EMON_CURRENT_RATIO);
  324. _sensors.push_back(sensor);
  325. }
  326. #endif
  327. #if EVENTS_SUPPORT
  328. {
  329. EventSensor * sensor = new EventSensor();
  330. sensor->setGPIO(EVENTS_PIN);
  331. sensor->setMode(EVENTS_PIN_MODE);
  332. sensor->setDebounceTime(EVENTS_DEBOUNCE);
  333. sensor->setInterruptMode(EVENTS_INTERRUPT_MODE);
  334. _sensors.push_back(sensor);
  335. }
  336. #endif
  337. #if HLW8012_SUPPORT
  338. {
  339. HLW8012Sensor * sensor = new HLW8012Sensor();
  340. sensor->setSEL(HLW8012_SEL_PIN);
  341. sensor->setCF(HLW8012_CF_PIN);
  342. sensor->setCF1(HLW8012_CF1_PIN);
  343. sensor->setSELCurrent(HLW8012_SEL_CURRENT);
  344. _sensors.push_back(sensor);
  345. }
  346. #endif
  347. #if MHZ19_SUPPORT
  348. {
  349. MHZ19Sensor * sensor = new MHZ19Sensor();
  350. sensor->setRX(MHZ19_RX_PIN);
  351. sensor->setTX(MHZ19_TX_PIN);
  352. _sensors.push_back(sensor);
  353. }
  354. #endif
  355. #if PMSX003_SUPPORT
  356. {
  357. PMSX003Sensor * sensor = new PMSX003Sensor();
  358. sensor->setRX(PMS_RX_PIN);
  359. sensor->setTX(PMS_TX_PIN);
  360. _sensors.push_back(sensor);
  361. }
  362. #endif
  363. #if PZEM004T_SUPPORT
  364. {
  365. PZEM004TSensor * sensor = new PZEM004TSensor();
  366. #if PZEM004T_USE_SOFT
  367. sensor->setRX(PZEM004T_RX_PIN);
  368. sensor->setTX(PZEM004T_TX_PIN);
  369. #else
  370. sensor->setSerial(& PZEM004T_HW_PORT);
  371. #endif
  372. _sensors.push_back(sensor);
  373. }
  374. #endif
  375. #if SHT3X_I2C_SUPPORT
  376. {
  377. SHT3XI2CSensor * sensor = new SHT3XI2CSensor();
  378. sensor->setAddress(SHT3X_I2C_ADDRESS);
  379. _sensors.push_back(sensor);
  380. }
  381. #endif
  382. #if SI7021_SUPPORT
  383. {
  384. SI7021Sensor * sensor = new SI7021Sensor();
  385. sensor->setAddress(SI7021_ADDRESS);
  386. _sensors.push_back(sensor);
  387. }
  388. #endif
  389. #if V9261F_SUPPORT
  390. {
  391. V9261FSensor * sensor = new V9261FSensor();
  392. sensor->setRX(V9261F_PIN);
  393. sensor->setInverted(V9261F_PIN_INVERSE);
  394. _sensors.push_back(sensor);
  395. }
  396. #endif
  397. #if AM2320_SUPPORT
  398. {
  399. AM2320Sensor * sensor = new AM2320Sensor();
  400. sensor->setAddress(AM2320_ADDRESS);
  401. _sensors.push_back(sensor);
  402. }
  403. #endif
  404. #if GUVAS12SD_SUPPORT
  405. {
  406. GUVAS12SDSensor * sensor = new GUVAS12SDSensor();
  407. sensor->setGPIO(GUVAS12SD_PIN);
  408. _sensors.push_back(sensor);
  409. }
  410. #endif
  411. }
  412. void _sensorCallback(unsigned char i, unsigned char type, const char * payload) {
  413. DEBUG_MSG_P(PSTR("[SENSOR] Sensor #%u callback, type %u, payload: '%s'\n"), i, type, payload);
  414. }
  415. void _sensorInit() {
  416. _sensors_ready = true;
  417. for (unsigned char i=0; i<_sensors.size(); i++) {
  418. // Do not process an already initialized sensor
  419. if (_sensors[i]->ready()) continue;
  420. DEBUG_MSG_P(PSTR("[SENSOR] Initializing %s\n"), _sensors[i]->description().c_str());
  421. // Force sensor to reload config
  422. _sensors[i]->begin();
  423. if (!_sensors[i]->ready()) {
  424. if (_sensors[i]->error() != 0) DEBUG_MSG_P(PSTR("[SENSOR] -> ERROR %d\n"), _sensors[i]->error());
  425. _sensors_ready = false;
  426. continue;
  427. }
  428. // Initialize magnitudes
  429. for (unsigned char k=0; k<_sensors[i]->count(); k++) {
  430. unsigned char type = _sensors[i]->type(k);
  431. sensor_magnitude_t new_magnitude;
  432. new_magnitude.sensor = _sensors[i];
  433. new_magnitude.local = k;
  434. new_magnitude.type = type;
  435. new_magnitude.global = _counts[type];
  436. new_magnitude.current = 0;
  437. new_magnitude.filtered = 0;
  438. new_magnitude.reported = 0;
  439. new_magnitude.min_change = 0;
  440. if (type == MAGNITUDE_DIGITAL) {
  441. new_magnitude.filter = new MaxFilter();
  442. } else if (type == MAGNITUDE_EVENTS) {
  443. new_magnitude.filter = new MovingAverageFilter();
  444. } else {
  445. new_magnitude.filter = new MedianFilter();
  446. }
  447. new_magnitude.filter->resize(_sensor_report_every);
  448. _magnitudes.push_back(new_magnitude);
  449. DEBUG_MSG_P(PSTR("[SENSOR] -> %s:%d\n"), magnitudeTopic(type).c_str(), _counts[type]);
  450. _counts[type] = _counts[type] + 1;
  451. }
  452. // Hook callback
  453. _sensors[i]->onEvent([i](unsigned char type, const char * payload) {
  454. _sensorCallback(i, type, payload);
  455. });
  456. // Custom initializations
  457. #if EMON_ANALOG_SUPPORT
  458. if (_sensors[i]->getID() == SENSOR_EMON_ANALOG_ID) {
  459. EmonAnalogSensor * sensor = (EmonAnalogSensor *) _sensors[i];
  460. sensor->setCurrentRatio(0, getSetting("pwrRatioC", EMON_CURRENT_RATIO).toFloat());
  461. sensor->setVoltage(getSetting("pwrVoltage", EMON_MAINS_VOLTAGE).toInt());
  462. }
  463. #endif // EMON_ANALOG_SUPPORT
  464. #if HLW8012_SUPPORT
  465. if (_sensors[i]->getID() == SENSOR_HLW8012_ID) {
  466. HLW8012Sensor * sensor = (HLW8012Sensor *) _sensors[i];
  467. double value;
  468. value = getSetting("pwrRatioC", 0).toFloat();
  469. if (value > 0) sensor->setCurrentRatio(value);
  470. value = getSetting("pwrRatioV", 0).toFloat();
  471. if (value > 0) sensor->setVoltageRatio(value);
  472. value = getSetting("pwrRatioP", 0).toFloat();
  473. if (value > 0) sensor->setPowerRatio(value);
  474. }
  475. #endif // HLW8012_SUPPORT
  476. }
  477. }
  478. void _sensorConfigure() {
  479. // General sensor settings
  480. _sensor_read_interval = 1000 * constrain(getSetting("snsRead", SENSOR_READ_INTERVAL).toInt(), SENSOR_READ_MIN_INTERVAL, SENSOR_READ_MAX_INTERVAL);
  481. _sensor_report_every = constrain(getSetting("snsReport", SENSOR_REPORT_EVERY).toInt(), SENSOR_REPORT_MIN_EVERY, SENSOR_REPORT_MAX_EVERY);
  482. _sensor_realtime = getSetting("apiRealTime", API_REAL_TIME_VALUES).toInt() == 1;
  483. _sensor_power_units = getSetting("pwrUnits", SENSOR_POWER_UNITS).toInt();
  484. _sensor_energy_units = getSetting("energyUnits", SENSOR_ENERGY_UNITS).toInt();
  485. _sensor_temperature_units = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt();
  486. _sensor_temperature_correction = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION).toFloat();
  487. _sensor_humidity_correction = getSetting("humCorrection", SENSOR_HUMIDITY_CORRECTION).toFloat();
  488. // Specific sensor settings
  489. for (unsigned char i=0; i<_sensors.size(); i++) {
  490. #if EMON_ANALOG_SUPPORT
  491. if (_sensors[i]->getID() == SENSOR_EMON_ANALOG_ID) {
  492. double value;
  493. EmonAnalogSensor * sensor = (EmonAnalogSensor *) _sensors[i];
  494. if (value = getSetting("pwrExpectedP", 0).toInt()) {
  495. sensor->expectedPower(0, value);
  496. setSetting("pwrRatioC", sensor->getCurrentRatio(0));
  497. }
  498. if (getSetting("pwrResetCalibration", 0).toInt() == 1) {
  499. sensor->setCurrentRatio(0, EMON_CURRENT_RATIO);
  500. delSetting("pwrRatioC");
  501. }
  502. if (getSetting("pwrResetE", 0).toInt() == 1) {
  503. sensor->resetEnergy();
  504. }
  505. sensor->setVoltage(getSetting("pwrVoltage", EMON_MAINS_VOLTAGE).toInt());
  506. }
  507. #endif // EMON_ANALOG_SUPPORT
  508. #if EMON_ADC121_SUPPORT
  509. if (_sensors[i]->getID() == SENSOR_EMON_ADC121_ID) {
  510. EmonADC121Sensor * sensor = (EmonADC121Sensor *) _sensors[i];
  511. if (getSetting("pwrResetE", 0).toInt() == 1) {
  512. sensor->resetEnergy();
  513. }
  514. }
  515. #endif
  516. #if EMON_ADS1X15_SUPPORT
  517. if (_sensors[i]->getID() == SENSOR_EMON_ADS1X15_ID) {
  518. EmonADS1X15Sensor * sensor = (EmonADS1X15Sensor *) _sensors[i];
  519. if (getSetting("pwrResetE", 0).toInt() == 1) {
  520. sensor->resetEnergy();
  521. }
  522. }
  523. #endif
  524. #if HLW8012_SUPPORT
  525. if (_sensors[i]->getID() == SENSOR_HLW8012_ID) {
  526. double value;
  527. HLW8012Sensor * sensor = (HLW8012Sensor *) _sensors[i];
  528. if (value = getSetting("pwrExpectedC", 0).toFloat()) {
  529. sensor->expectedCurrent(value);
  530. setSetting("pwrRatioC", sensor->getCurrentRatio());
  531. }
  532. if (value = getSetting("pwrExpectedV", 0).toInt()) {
  533. sensor->expectedVoltage(value);
  534. setSetting("pwrRatioV", sensor->getVoltageRatio());
  535. }
  536. if (value = getSetting("pwrExpectedP", 0).toInt()) {
  537. sensor->expectedPower(value);
  538. setSetting("pwrRatioP", sensor->getPowerRatio());
  539. }
  540. if (getSetting("pwrResetE", 0).toInt() == 1) {
  541. sensor->resetEnergy();
  542. }
  543. if (getSetting("pwrResetCalibration", 0).toInt() == 1) {
  544. sensor->resetRatios();
  545. delSetting("pwrRatioC");
  546. delSetting("pwrRatioV");
  547. delSetting("pwrRatioP");
  548. }
  549. }
  550. #endif // HLW8012_SUPPORT
  551. }
  552. // Update filter sizes
  553. for (unsigned char i=0; i<_magnitudes.size(); i++) {
  554. _magnitudes[i].filter->resize(_sensor_report_every);
  555. }
  556. // Save settings
  557. delSetting("pwrExpectedP");
  558. delSetting("pwrExpectedC");
  559. delSetting("pwrExpectedV");
  560. delSetting("pwrResetCalibration");
  561. delSetting("pwrResetE");
  562. saveSettings();
  563. }
  564. // -----------------------------------------------------------------------------
  565. // Public
  566. // -----------------------------------------------------------------------------
  567. unsigned char sensorCount() {
  568. return _sensors.size();
  569. }
  570. unsigned char magnitudeCount() {
  571. return _magnitudes.size();
  572. }
  573. String magnitudeName(unsigned char index) {
  574. if (index < _magnitudes.size()) {
  575. sensor_magnitude_t magnitude = _magnitudes[index];
  576. return magnitude.sensor->slot(magnitude.local);
  577. }
  578. return String();
  579. }
  580. unsigned char magnitudeType(unsigned char index) {
  581. if (index < _magnitudes.size()) {
  582. return int(_magnitudes[index].type);
  583. }
  584. return MAGNITUDE_NONE;
  585. }
  586. unsigned char magnitudeIndex(unsigned char index) {
  587. if (index < _magnitudes.size()) {
  588. return int(_magnitudes[index].global);
  589. }
  590. return 0;
  591. }
  592. String magnitudeTopic(unsigned char type) {
  593. char buffer[16] = {0};
  594. if (type < MAGNITUDE_MAX) strncpy_P(buffer, magnitude_topics[type], sizeof(buffer));
  595. return String(buffer);
  596. }
  597. String magnitudeTopicIndex(unsigned char index) {
  598. char topic[32] = {0};
  599. if (index < _magnitudes.size()) {
  600. sensor_magnitude_t magnitude = _magnitudes[index];
  601. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
  602. snprintf(topic, sizeof(topic), "%s/%u", magnitudeTopic(magnitude.type).c_str(), magnitude.global);
  603. } else {
  604. snprintf(topic, sizeof(topic), "%s", magnitudeTopic(magnitude.type).c_str());
  605. }
  606. }
  607. return String(topic);
  608. }
  609. String magnitudeUnits(unsigned char type) {
  610. char buffer[8] = {0};
  611. if (type < MAGNITUDE_MAX) {
  612. if ((type == MAGNITUDE_TEMPERATURE) && (_sensor_temperature_units == TMP_FAHRENHEIT)) {
  613. strncpy_P(buffer, magnitude_fahrenheit, sizeof(buffer));
  614. } else if (
  615. (type == MAGNITUDE_ENERGY || type == MAGNITUDE_ENERGY_DELTA) &&
  616. (_sensor_energy_units == ENERGY_KWH)) {
  617. strncpy_P(buffer, magnitude_kwh, sizeof(buffer));
  618. } else if (
  619. (type == MAGNITUDE_POWER_ACTIVE || type == MAGNITUDE_POWER_APPARENT || type == MAGNITUDE_POWER_REACTIVE) &&
  620. (_sensor_power_units == POWER_KILOWATTS)) {
  621. strncpy_P(buffer, magnitude_kw, sizeof(buffer));
  622. } else {
  623. strncpy_P(buffer, magnitude_units[type], sizeof(buffer));
  624. }
  625. }
  626. return String(buffer);
  627. }
  628. // -----------------------------------------------------------------------------
  629. void sensorSetup() {
  630. // Backwards compatibility
  631. moveSetting("powerUnits", "pwrUnits");
  632. // Load sensors
  633. _sensorLoad();
  634. _sensorInit();
  635. // Configure stored values
  636. _sensorConfigure();
  637. #if WEB_SUPPORT
  638. // Websockets
  639. wsOnSendRegister(_sensorWebSocketStart);
  640. wsOnReceiveRegister(_sensorWebSocketOnReceive);
  641. wsOnSendRegister(_sensorWebSocketSendData);
  642. wsOnAfterParseRegister(_sensorConfigure);
  643. // API
  644. _sensorAPISetup();
  645. #endif
  646. #if TERMINAL_SUPPORT
  647. _sensorInitCommands();
  648. #endif
  649. // Register loop
  650. espurnaRegisterLoop(sensorLoop);
  651. }
  652. void sensorLoop() {
  653. // Check if we still have uninitialized sensors
  654. static unsigned long last_init = 0;
  655. if (!_sensors_ready) {
  656. if (millis() - last_init > SENSOR_INIT_INTERVAL) {
  657. last_init = millis();
  658. _sensorInit();
  659. }
  660. }
  661. if (_magnitudes.size() == 0) return;
  662. // Tick hook
  663. _sensorTick();
  664. // Check if we should read new data
  665. static unsigned long last_update = 0;
  666. static unsigned long report_count = 0;
  667. if (millis() - last_update > _sensor_read_interval) {
  668. last_update = millis();
  669. report_count = (report_count + 1) % _sensor_report_every;
  670. double current;
  671. double filtered;
  672. char buffer[64];
  673. // Pre-read hook
  674. _sensorPre();
  675. // Get the first relay state
  676. #if SENSOR_POWER_CHECK_STATUS
  677. bool relay_off = (relayCount() > 0) && (relayStatus(0) == 0);
  678. #endif
  679. // Get readings
  680. for (unsigned char i=0; i<_magnitudes.size(); i++) {
  681. sensor_magnitude_t magnitude = _magnitudes[i];
  682. if (magnitude.sensor->status()) {
  683. current = magnitude.sensor->value(magnitude.local);
  684. // Completely remove spurious values if relay is OFF
  685. #if SENSOR_POWER_CHECK_STATUS
  686. if (relay_off) {
  687. if (magnitude.type == MAGNITUDE_POWER_ACTIVE ||
  688. magnitude.type == MAGNITUDE_POWER_REACTIVE ||
  689. magnitude.type == MAGNITUDE_POWER_APPARENT ||
  690. magnitude.type == MAGNITUDE_CURRENT ||
  691. magnitude.type == MAGNITUDE_ENERGY_DELTA
  692. ) {
  693. current = 0;
  694. }
  695. }
  696. #endif
  697. magnitude.filter->add(current);
  698. // Special case
  699. if (magnitude.type == MAGNITUDE_EVENTS) {
  700. current = magnitude.filter->result();
  701. }
  702. current = _magnitudeProcess(magnitude.type, current);
  703. _magnitudes[i].current = current;
  704. unsigned char decimals = _magnitudeDecimals(magnitude.type);
  705. // Debug
  706. #if SENSOR_DEBUG
  707. {
  708. dtostrf(current, 1-sizeof(buffer), decimals, buffer);
  709. DEBUG_MSG_P(PSTR("[SENSOR] %s - %s: %s%s\n"),
  710. magnitude.sensor->slot(magnitude.local).c_str(),
  711. magnitudeTopic(magnitude.type).c_str(),
  712. buffer,
  713. magnitudeUnits(magnitude.type).c_str()
  714. );
  715. }
  716. #endif // SENSOR_DEBUG
  717. // Time to report (we do it every _sensor_report_every readings)
  718. if (report_count == 0) {
  719. filtered = magnitude.filter->result();
  720. magnitude.filter->reset();
  721. filtered = _magnitudeProcess(magnitude.type, filtered);
  722. _magnitudes[i].filtered = filtered;
  723. // Check if there is a minimum change threshold to report
  724. if (fabs(filtered - magnitude.reported) >= magnitude.min_change) {
  725. _magnitudes[i].reported = filtered;
  726. dtostrf(filtered, 1-sizeof(buffer), decimals, buffer);
  727. #if BROKER_SUPPORT
  728. brokerPublish(magnitudeTopic(magnitude.type).c_str(), magnitude.local, buffer);
  729. #endif
  730. #if MQTT_SUPPORT
  731. mqttSend(magnitudeTopicIndex(i).c_str(), buffer);
  732. #if SENSOR_PUBLISH_ADDRESSES
  733. char topic[32];
  734. snprintf(topic, sizeof(topic), "%s/%s", SENSOR_ADDRESS_TOPIC, magnitudeTopic(magnitude.type).c_str());
  735. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
  736. mqttSend(topic, magnitude.global, magnitude.sensor->address(magnitude.local).c_str());
  737. } else {
  738. mqttSend(topic, magnitude.sensor->address(magnitude.local).c_str());
  739. }
  740. #endif // SENSOR_PUBLISH_ADDRESSES
  741. #endif // MQTT_SUPPORT
  742. #if INFLUXDB_SUPPORT
  743. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
  744. idbSend(magnitudeTopic(magnitude.type).c_str(), magnitude.global, buffer);
  745. } else {
  746. idbSend(magnitudeTopic(magnitude.type).c_str(), buffer);
  747. }
  748. #endif // INFLUXDB_SUPPORT
  749. #if THINGSPEAK_SUPPORT
  750. tspkEnqueueMeasurement(i, buffer);
  751. #endif
  752. #if DOMOTICZ_SUPPORT
  753. {
  754. char key[15];
  755. snprintf_P(key, sizeof(key), PSTR("dczMagnitude%d"), i);
  756. if (magnitude.type == MAGNITUDE_HUMIDITY) {
  757. int status;
  758. if (filtered > 70) {
  759. status = HUMIDITY_WET;
  760. } else if (filtered > 45) {
  761. status = HUMIDITY_COMFORTABLE;
  762. } else if (filtered > 30) {
  763. status = HUMIDITY_NORMAL;
  764. } else {
  765. status = HUMIDITY_DRY;
  766. }
  767. char status_buf[5];
  768. itoa(status, status_buf, 10);
  769. domoticzSend(key, buffer, status_buf);
  770. } else {
  771. domoticzSend(key, 0, buffer);
  772. }
  773. }
  774. #endif // DOMOTICZ_SUPPORT
  775. } // if (fabs(filtered - magnitude.reported) >= magnitude.min_change)
  776. } // if (report_count == 0)
  777. } // if (magnitude.sensor->status())
  778. } // for (unsigned char i=0; i<_magnitudes.size(); i++)
  779. // Post-read hook
  780. _sensorPost();
  781. #if WEB_SUPPORT
  782. wsSend(_sensorWebSocketSendData);
  783. #endif
  784. #if THINGSPEAK_SUPPORT
  785. if (report_count == 0) tspkFlush();
  786. #endif
  787. }
  788. }
  789. #endif // SENSOR_SUPPORT