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.

344 lines
10 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 "filters/MedianFilter.h"
  7. #include "filters/MovingAverageFilter.h"
  8. #include "sensors/BaseSensor.h"
  9. typedef struct {
  10. BaseSensor * 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. BaseFilter * filter; // Filter object
  17. } sensor_magnitude_t;
  18. std::vector<BaseSensor *> _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. unsigned char _sensor_isr = 0xFF;
  25. // -----------------------------------------------------------------------------
  26. // Private
  27. // -----------------------------------------------------------------------------
  28. String _sensorTopic(magnitude_t type) {
  29. if (type == MAGNITUDE_TEMPERATURE) {
  30. return String(SENSOR_TEMPERATURE_TOPIC);
  31. } else if (type == MAGNITUDE_HUMIDITY) {
  32. return String(SENSOR_HUMIDITY_TOPIC);
  33. } else if (type == MAGNITUDE_ANALOG) {
  34. return String(SENSOR_ANALOG_TOPIC);
  35. } else if (type == MAGNITUDE_EVENTS) {
  36. return String(SENSOR_EVENTS_TOPIC);
  37. }
  38. return String(SENSOR_UNKNOWN_TOPIC);
  39. }
  40. unsigned char _sensorDecimals(magnitude_t type) {
  41. if (type == MAGNITUDE_TEMPERATURE) {
  42. return SENSOR_TEMPERATURE_DECIMALS;
  43. } else if (type == MAGNITUDE_HUMIDITY) {
  44. return SENSOR_HUMIDITY_DECIMALS;
  45. } else if (type == MAGNITUDE_ANALOG) {
  46. return SENSOR_ANALOG_DECIMALS;
  47. } else if (type == MAGNITUDE_EVENTS) {
  48. return SENSOR_EVENTS_DECIMALS;
  49. }
  50. return 0;
  51. }
  52. String _sensorUnits(magnitude_t type) {
  53. if (type == MAGNITUDE_TEMPERATURE) {
  54. if (_sensor_temperature_units == TMP_CELSIUS) {
  55. return String("C");
  56. } else {
  57. return String("F");
  58. }
  59. } else if (type == MAGNITUDE_HUMIDITY) {
  60. return String("%");
  61. } else if (type == MAGNITUDE_EVENTS) {
  62. return String("/m");
  63. }
  64. return String();
  65. }
  66. double _sensorProcess(magnitude_t type, double value) {
  67. if (type == MAGNITUDE_TEMPERATURE) {
  68. if (_sensor_temperature_units == TMP_FAHRENHEIT) value = value * 1.8 + 32;
  69. value = value + _sensor_temperature_correction;
  70. }
  71. return roundTo(value, _sensorDecimals(type));
  72. }
  73. void _sensorConfigure() {
  74. _sensor_realtime = getSetting("apiRealTime", API_REAL_TIME_VALUES).toInt() == 1;
  75. _sensor_temperature_units = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt();
  76. _sensor_temperature_correction = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION).toFloat();
  77. }
  78. #if WEB_SUPPORT
  79. void _sensorWebSocketOnSend(JsonObject& root) {
  80. char buffer[10];
  81. bool hasTemperature = false;
  82. JsonArray& sensors = root.createNestedArray("sensors");
  83. for (unsigned char i=0; i<_magnitudes.size(); i++) {
  84. sensor_magnitude_t magnitude = _magnitudes[i];
  85. unsigned char decimals = _sensorDecimals(magnitude.type);
  86. dtostrf(magnitude.current, 1-sizeof(buffer), decimals, buffer);
  87. JsonObject& sensor = sensors.createNestedObject();
  88. sensor["type"] = int(magnitude.type);
  89. sensor["value"] = String(buffer);
  90. sensor["units"] = _sensorUnits(magnitude.type);
  91. sensor["description"] = magnitude.sensor->slot(magnitude.local);
  92. if (magnitude.type == MAGNITUDE_TEMPERATURE) hasTemperature = true;
  93. }
  94. //root["apiRealTime"] = _sensor_realtime;
  95. root["tmpUnits"] = _sensor_temperature_units;
  96. root["tmpCorrection"] = _sensor_temperature_correction;
  97. if (hasTemperature) root["temperatureVisible"] = 1;
  98. }
  99. void _sensorAPISetup() {
  100. for (unsigned char magnitude_id=0; magnitude_id<_magnitudes.size(); magnitude_id++) {
  101. sensor_magnitude_t magnitude = _magnitudes[magnitude_id];
  102. String topic = _sensorTopic(magnitude.type);
  103. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) topic = topic + "/" + String(magnitude.global);
  104. apiRegister(topic.c_str(), topic.c_str(), [magnitude_id](char * buffer, size_t len) {
  105. sensor_magnitude_t magnitude = _magnitudes[magnitude_id];
  106. unsigned char decimals = _sensorDecimals(magnitude.type);
  107. double value = _sensor_realtime ? magnitude.current : magnitude.filtered;
  108. dtostrf(value, 1-len, decimals, buffer);
  109. });
  110. }
  111. }
  112. #endif
  113. // -----------------------------------------------------------------------------
  114. // Values
  115. // -----------------------------------------------------------------------------
  116. void sensorISR() {
  117. _sensors[_sensor_isr]->InterruptHandler();
  118. }
  119. void sensorRegister(BaseSensor * sensor) {
  120. _sensors.push_back(sensor);
  121. }
  122. unsigned char sensorCount() {
  123. return _sensors.size();
  124. }
  125. void sensorInterrupt(unsigned char sensor_id, unsigned char gpio, int mode) {
  126. _sensor_isr = sensor_id;
  127. attachInterrupt(gpio, sensorISR, mode);
  128. }
  129. void sensorInit() {
  130. #if DHT_SUPPORT
  131. #include "sensors/DHTSensor.h"
  132. sensorRegister(new DHTSensor(DHT_PIN, DHT_TYPE, DHT_PULLUP));
  133. #endif
  134. #if DS18B20_SUPPORT
  135. #include "sensors/DS18B20Sensor.h"
  136. sensorRegister(new DS18B20Sensor(DS18B20_PIN, DS18B20_PULLUP));
  137. #endif
  138. #if ANALOG_SUPPORT
  139. #include "sensors/AnalogSensor.h"
  140. sensorRegister(new AnalogSensor(ANALOG_PIN));
  141. #endif
  142. #if COUNTER_SUPPORT
  143. if (_sensor_isr == 0xFF) {
  144. #include "sensors/EventSensor.h"
  145. sensorRegister(new EventSensor(COUNTER_PIN, COUNTER_PIN_MODE, COUNTER_DEBOUNCE));
  146. sensorInterrupt(sensorCount()-1, COUNTER_PIN, COUNTER_INTERRUPT_MODE);
  147. }
  148. #endif
  149. }
  150. void sensorSetup() {
  151. // Load sensors
  152. sensorInit();
  153. // Load magnitudes
  154. for (unsigned char i=0; i<_sensors.size(); i++) {
  155. BaseSensor * sensor = _sensors[i];
  156. DEBUG_MSG("[SENSOR] %s\n", sensor->name().c_str());
  157. for (unsigned char k=0; k<sensor->count(); k++) {
  158. magnitude_t type = sensor->type(k);
  159. sensor_magnitude_t new_magnitude;
  160. new_magnitude.sensor = sensor;
  161. new_magnitude.local = k;
  162. new_magnitude.type = type;
  163. new_magnitude.global = _counts[type];
  164. new_magnitude.current = 0;
  165. new_magnitude.filtered = 0;
  166. if (type == MAGNITUDE_EVENTS) {
  167. new_magnitude.filter = new MovingAverageFilter(SENSOR_REPORT_EVERY);
  168. } else {
  169. new_magnitude.filter = new MedianFilter();
  170. }
  171. _magnitudes.push_back(new_magnitude);
  172. DEBUG_MSG("[SENSOR] -> %s:%d\n", _sensorTopic(type).c_str(), _counts[type]);
  173. _counts[type] = _counts[type] + 1;
  174. }
  175. }
  176. #if WEB_SUPPORT
  177. // Websockets
  178. wsOnSendRegister(_sensorWebSocketOnSend);
  179. wsOnAfterParseRegister(_sensorConfigure);
  180. // API
  181. _sensorAPISetup();
  182. #endif
  183. }
  184. void sensorLoop() {
  185. static unsigned long last_update = 0;
  186. static unsigned long report_count = 0;
  187. // Check if we should read new data
  188. if ((millis() - last_update > SENSOR_READ_INTERVAL) || (last_update == 0)) {
  189. last_update = millis();
  190. report_count = (report_count + 1) % SENSOR_REPORT_EVERY;
  191. double value;
  192. char buffer[64];
  193. // Pre-read hook
  194. for (unsigned char i=0; i<_sensors.size(); i++) {
  195. _sensors[i]->pre();
  196. if (!_sensors[i]->status()) {
  197. DEBUG_MSG("[SENSOR] Error reading data from %s (error: %d)\n",
  198. _sensors[i]->name().c_str(),
  199. _sensors[i]->error()
  200. );
  201. }
  202. }
  203. // Get readings
  204. for (unsigned char i=0; i<_magnitudes.size(); i++) {
  205. sensor_magnitude_t magnitude = _magnitudes[i];
  206. if (magnitude.sensor->status()) {
  207. unsigned char decimals = _sensorDecimals(magnitude.type);
  208. value = magnitude.sensor->value(magnitude.local);
  209. magnitude.filter->add(value);
  210. // Special case
  211. if (magnitude.type == MAGNITUDE_EVENTS) value = magnitude.filter->result();
  212. value = _sensorProcess(magnitude.type, value);
  213. _magnitudes[i].current = value;
  214. // Debug
  215. /*
  216. {
  217. dtostrf(value, 1-sizeof(buffer), decimals, buffer);
  218. DEBUG_MSG("[SENSOR] %s - %s: %s%s\n",
  219. magnitude.sensor->name().c_str(),
  220. _sensorTopic(magnitude.type).c_str(),
  221. buffer,
  222. _sensorUnits(magnitude.type).c_str()
  223. );
  224. }
  225. */
  226. if (report_count == 0) {
  227. // TODO: option to report only if it has change (configurable amount)
  228. value = magnitude.filter->result();
  229. value = _sensorProcess(magnitude.type, value);
  230. _magnitudes[i].filtered = value;
  231. magnitude.filter->reset();
  232. dtostrf(value, 1-sizeof(buffer), decimals, buffer);
  233. #if MQTT_SUPPORT
  234. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
  235. mqttSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
  236. } else {
  237. mqttSend(_sensorTopic(magnitude.type).c_str(), buffer);
  238. }
  239. #endif
  240. #if INFLUXDB_SUPPORT
  241. if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
  242. idbSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
  243. } else {
  244. idbSend(_sensorTopic(magnitude.type).c_str(), buffer);
  245. }
  246. #endif
  247. #if DOMOTICZ_SUPPORT
  248. // TODO
  249. #endif
  250. }
  251. }
  252. }
  253. // Post-read hook
  254. for (unsigned char i=0; i<_sensors.size(); i++) {
  255. _sensors[i]->post();
  256. }
  257. #if WEB_SUPPORT
  258. wsSend(_sensorWebSocketOnSend);
  259. #endif
  260. }
  261. }