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.

290 lines
8.9 KiB

  1. /*
  2. SENSOR MODULE
  3. Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #include <vector>
  6. #include "libs/AggregatorMedian.h"
  7. #include "libs/AggregatorMovingAverage.h"
  8. #include "sensors/SensorBase.h"
  9. typedef struct {
  10. SensorBase * sensor;
  11. unsigned char local; // Local index in its provider
  12. magnitude_t type; // Type of measurement
  13. unsigned char global; // Global index in its type
  14. double current; // Current (last) value, unfiltered
  15. double filtered; // Filtered (averaged) value
  16. AggregatorBase * filter; // Filter object
  17. } sensor_magnitude_t;
  18. std::vector<SensorBase *> _sensors;
  19. std::vector<sensor_magnitude_t> _magnitudes;
  20. unsigned char _counts[MAGNITUDE_MAX];
  21. bool _sensor_realtime = API_REAL_TIME_VALUES;
  22. unsigned char _sensor_temperature_units = SENSOR_TEMPERATURE_UNITS;
  23. double _sensor_temperature_correction = SENSOR_TEMPERATURE_CORRECTION;
  24. #if DHT_SUPPORT
  25. #include "sensors/SensorDHT.h"
  26. #endif
  27. // -----------------------------------------------------------------------------
  28. // Private
  29. // -----------------------------------------------------------------------------
  30. String _sensorTopic(magnitude_t type) {
  31. if (type == MAGNITUDE_TEMPERATURE) {
  32. return String(SENSOR_TEMPERATURE_TOPIC);
  33. } else if (type == MAGNITUDE_HUMIDITY) {
  34. return String(SENSOR_HUMIDITY_TOPIC);
  35. }
  36. return String(SENSOR_UNKNOWN_TOPIC);
  37. }
  38. unsigned char _sensorDecimals(magnitude_t type) {
  39. if (type == MAGNITUDE_TEMPERATURE) {
  40. return SENSOR_TEMPERATURE_DECIMALS;
  41. } else if (type == MAGNITUDE_HUMIDITY) {
  42. return SENSOR_HUMIDITY_DECIMALS;
  43. }
  44. return 0;
  45. }
  46. String _sensorUnits(magnitude_t type) {
  47. if (type == MAGNITUDE_TEMPERATURE) {
  48. if (_sensor_temperature_units == TMP_CELSIUS) {
  49. return String("C");
  50. } else {
  51. return String("F");
  52. }
  53. } else if (type == MAGNITUDE_HUMIDITY) {
  54. return String("%");
  55. }
  56. return String();
  57. }
  58. double _sensorProcess(magnitude_t type, double value) {
  59. if (type == MAGNITUDE_TEMPERATURE) {
  60. if (_sensor_temperature_units == TMP_FAHRENHEIT) value = value * 1.8 + 32;
  61. value = value + _sensor_temperature_correction;
  62. }
  63. return roundTo(value, _sensorDecimals(type));
  64. }
  65. void _sensorConfigure() {
  66. _sensor_realtime = getSetting("apiRealTime", API_REAL_TIME_VALUES).toInt() == 1;
  67. _sensor_temperature_units = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt();
  68. _sensor_temperature_correction = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION).toFloat();
  69. }
  70. #if WEB_SUPPORT
  71. void _sensorWebSocketOnSend(JsonObject& root) {
  72. bool hasTemperature = false;
  73. JsonArray& sensors = root.createNestedArray("sensors");
  74. for (unsigned char i=0; i<_magnitudes.size(); i++) {
  75. sensor_magnitude_t magnitude = _magnitudes[i];
  76. JsonObject& sensor = sensors.createNestedObject();
  77. sensor["type"] = int(magnitude.type);
  78. sensor["value"] = magnitude.current;
  79. sensor["units"] = _sensorUnits(magnitude.type);
  80. sensor["description"] = magnitude.sensor->slot(magnitude.local);
  81. if (magnitude.type == MAGNITUDE_TEMPERATURE) hasTemperature = true;
  82. }
  83. //root["apiRealTime"] = _sensor_realtime;
  84. root["tmpUnits"] = _sensor_temperature_units;
  85. root["tmpCorrection"] = _sensor_temperature_correction;
  86. if (hasTemperature) root["temperatureVisible"] = 1;
  87. }
  88. void _sensorAPISetup() {
  89. for (unsigned char magnitude_id=0; magnitude_id<_magnitudes.size(); magnitude_id++) {
  90. sensor_magnitude_t magnitude = _magnitudes[magnitude_id];
  91. String topic = _sensorTopic(magnitude.type);
  92. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) topic = topic + "/" + String(magnitude.global);
  93. apiRegister(topic.c_str(), topic.c_str(), [magnitude_id](char * buffer, size_t len) {
  94. sensor_magnitude_t magnitude = _magnitudes[magnitude_id];
  95. unsigned char decimals = _sensorDecimals(magnitude.type);
  96. double value = _sensor_realtime ? magnitude.current : magnitude.filtered;
  97. dtostrf(value, 1-len, decimals, buffer);
  98. });
  99. }
  100. }
  101. #endif
  102. // -----------------------------------------------------------------------------
  103. // Values
  104. // -----------------------------------------------------------------------------
  105. void sensorSetup() {
  106. // Load sensors
  107. #if DHT_SUPPORT
  108. {
  109. _sensors.push_back(new SensorDHT(DHT_PIN, DHT_TYPE));
  110. #if DHT_PULLUP
  111. pinMode(DHT_PIN, INPUT_PULLUP);
  112. #endif
  113. }
  114. #endif
  115. // Read magnitudes
  116. for (unsigned char i=0; i<_sensors.size(); i++) {
  117. SensorBase * sensor = _sensors[i];
  118. DEBUG_MSG("[SENSOR] %s\n", sensor->name().c_str());
  119. for (unsigned char k=0; k<sensor->count(); k++) {
  120. magnitude_t type = sensor->type(k);
  121. sensor_magnitude_t new_magnitude;
  122. new_magnitude.sensor = sensor;
  123. new_magnitude.local = k;
  124. new_magnitude.type = type;
  125. new_magnitude.global = _counts[type];
  126. new_magnitude.current = 0;
  127. new_magnitude.filtered = 0;
  128. if (type == MAGNITUDE_EVENTS) {
  129. new_magnitude.filter = new AggregatorMovingAverage(SENSOR_REPORT_EVERY);
  130. } else {
  131. new_magnitude.filter = new AggregatorMedian();
  132. }
  133. _magnitudes.push_back(new_magnitude);
  134. DEBUG_MSG("[SENSOR] -> %s:%d\n", _sensorTopic(type).c_str(), _counts[type]);
  135. _counts[type] = _counts[type] + 1;
  136. }
  137. }
  138. #if WEB_SUPPORT
  139. // Websockets
  140. wsOnSendRegister(_sensorWebSocketOnSend);
  141. wsOnAfterParseRegister(_sensorConfigure);
  142. // API
  143. _sensorAPISetup();
  144. #endif
  145. }
  146. void sensorLoop() {
  147. static unsigned long last_update = 0;
  148. static unsigned long report_count = 0;
  149. // Check if we should read new data
  150. if ((millis() - last_update > SENSOR_READ_INTERVAL) || (last_update == 0)) {
  151. last_update = millis();
  152. report_count = (report_count + 1) % SENSOR_REPORT_EVERY;
  153. double value;
  154. char buffer[64];
  155. // Pre-read hook
  156. for (unsigned char i=0; i<_sensors.size(); i++) {
  157. _sensors[i]->pre();
  158. if (!_sensors[i]->status()) {
  159. DEBUG_MSG("[SENSOR] Error reading data from %s (error: %d)\n",
  160. _sensors[i]->name().c_str(),
  161. _sensors[i]->error()
  162. );
  163. }
  164. }
  165. // Get readings
  166. for (unsigned char i=0; i<_magnitudes.size(); i++) {
  167. sensor_magnitude_t magnitude = _magnitudes[i];
  168. if (magnitude.sensor->status()) {
  169. unsigned char decimals = _sensorDecimals(magnitude.type);
  170. value = magnitude.sensor->value(magnitude.local);
  171. magnitude.filter->add(value);
  172. value = _sensorProcess(magnitude.type, value);
  173. _magnitudes[i].current = value;
  174. // Debug
  175. /*
  176. {
  177. dtostrf(value, 1-sizeof(buffer), decimals, buffer);
  178. DEBUG_MSG("[SENSOR] %s - %s: %s%s\n",
  179. magnitude.sensor->name().c_str(),
  180. _sensorTopic(magnitude.type).c_str(),
  181. buffer,
  182. _sensorUnits(magnitude.type).c_str()
  183. );
  184. }
  185. */
  186. if (report_count == 0) {
  187. double value = magnitude.filter->result();
  188. value = _sensorProcess(magnitude.type, value);
  189. _magnitudes[i].filtered = value;
  190. magnitude.filter->reset();
  191. dtostrf(value, 1-sizeof(buffer), decimals, buffer);
  192. #if MQTT_SUPPORT
  193. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
  194. mqttSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
  195. } else {
  196. mqttSend(_sensorTopic(magnitude.type).c_str(), buffer);
  197. }
  198. #endif
  199. #if INFLUXDB_SUPPORT
  200. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
  201. idbSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
  202. } else {
  203. idbSend(_sensorTopic(magnitude.type).c_str(), buffer);
  204. }
  205. #endif
  206. #if DOMOTICZ_SUPPORT
  207. // TODO
  208. #endif
  209. }
  210. }
  211. }
  212. // Post-read hook
  213. for (unsigned char i=0; i<_sensors.size(); i++) {
  214. _sensors[i]->post();
  215. }
  216. #if WEB_SUPPORT
  217. wsSend(_sensorWebSocketOnSend);
  218. #endif
  219. }
  220. }