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.

324 lines
9.7 KiB

  1. // -----------------------------------------------------------------------------
  2. // SonoffSC peudo-sensor
  3. // Communicates with the ATMEGA328 onboard to retrieve
  4. // humidity, temperature, light, sound and dust values
  5. // Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com>
  6. // -----------------------------------------------------------------------------
  7. #if SENSOR_SUPPORT && SONOFFSC_SUPPORT
  8. #pragma once
  9. #include "Arduino.h"
  10. #include "BaseSensor.h"
  11. #define SONOFFSC_TERMINATION_CHAR 0x1B
  12. class SonoffSCSensor : public BaseSensor {
  13. public:
  14. // ---------------------------------------------------------------------
  15. // Public
  16. // ---------------------------------------------------------------------
  17. SonoffSCSensor(): BaseSensor() {
  18. _count = 3;
  19. #if SONOFFSC_HAS_LIGHT_REL
  20. ++_count;
  21. #endif
  22. #if SONOFFSC_HAS_DUST_REL
  23. ++_count;
  24. #endif
  25. #if SONOFFSC_HAS_MOVEMENT
  26. ++_count;
  27. #endif
  28. #if SONOFFSC_HAS_LUX
  29. ++_count;
  30. #endif
  31. #if SONOFFSC_HAS_DUST
  32. ++_count;
  33. #endif
  34. #if SONOFFSC_HAS_CLAP
  35. ++_count;
  36. #endif
  37. _sensor_id = SENSOR_SONOFFSC_ID;
  38. }
  39. // ---------------------------------------------------------------------
  40. // Sensor API
  41. // ---------------------------------------------------------------------
  42. // Initialization method, must be idempotent
  43. void begin() {
  44. Serial.begin(SONOFFSC_BAUDRATE);
  45. _send("AT+START");
  46. _sendConfig();
  47. _ready = true;
  48. }
  49. // Descriptive name of the sensor
  50. String description() {
  51. return String("SonoffSC @ HwSerial");
  52. }
  53. // Descriptive name of the slot # index
  54. String slot(unsigned char index) {
  55. return description();
  56. };
  57. // Address of the sensor (it could be the GPIO or I2C address)
  58. String address(unsigned char index) {
  59. return String("sc");
  60. }
  61. // Loop-like method, call it in your main loop
  62. void tick() {
  63. while (Serial.available()) {
  64. char ch = Serial.read();
  65. if (SONOFFSC_TERMINATION_CHAR == ch) {
  66. _buffer[_index] = 0;
  67. Serial.flush();
  68. _parse();
  69. _index = 0;
  70. } else {
  71. _buffer[_index] = ch;
  72. ++_index;
  73. if (SONOFFSC_BUFFER_SIZE == _index) _index = 0;
  74. }
  75. }
  76. }
  77. // Type for slot # index
  78. unsigned char type(unsigned char index) {
  79. if (index == 0) return MAGNITUDE_HUMIDITY;
  80. if (index == 1) return MAGNITUDE_TEMPERATURE;
  81. if (index == 2) return MAGNITUDE_NOISE_REL;
  82. unsigned char next = 3;
  83. #if SONOFFSC_HAS_LIGHT_REL
  84. if (index == next) return MAGNITUDE_LIGHT_REL;
  85. ++next;
  86. #endif
  87. #if SONOFFSC_HAS_DUST_REL
  88. if (index == next) return MAGNITUDE_DUST_REL;
  89. ++next;
  90. #endif
  91. #if SONOFFSC_HAS_MOVEMENT
  92. if (index == next) return MAGNITUDE_MOVEMENT;
  93. ++next;
  94. #endif
  95. #if SONOFFSC_HAS_LUX
  96. if (index == next) return MAGNITUDE_LUX;
  97. ++next;
  98. #endif
  99. #if SONOFFSC_HAS_DUST
  100. if (index == next) return MAGNITUDE_PM10;
  101. ++next;
  102. #endif
  103. #if SONOFFSC_HAS_CLAP
  104. if (index == next) return MAGNITUDE_EVENT;
  105. ++next;
  106. #endif
  107. return MAGNITUDE_NONE;
  108. }
  109. // Current value for slot # index
  110. double value(unsigned char index) {
  111. double response = 0;
  112. if (index == 0) return _humidity;
  113. if (index == 1) return _temperature;
  114. if (index == 2) return _noise_rel;
  115. unsigned char next = 3;
  116. #if SONOFFSC_HAS_LIGHT_REL
  117. if (index == next) return _light_rel;
  118. ++next;
  119. #endif
  120. #if SONOFFSC_HAS_DUST_REL
  121. if (index == next) return _dust_rel;
  122. ++next;
  123. #endif
  124. #if SONOFFSC_HAS_MOVEMENT
  125. if (index == next) return _movement;
  126. ++next;
  127. #endif
  128. #if SONOFFSC_HAS_LUX
  129. if (index == next) return _lux;
  130. ++next;
  131. #endif
  132. #if SONOFFSC_HAS_DUST
  133. if (index == next) return _dust;
  134. ++next;
  135. #endif
  136. #if SONOFFSC_HAS_CLAP
  137. if (index == next) return _clap;
  138. ++next;
  139. #endif
  140. return response;
  141. }
  142. protected:
  143. // ---------------------------------------------------------------------
  144. // Protected
  145. // ---------------------------------------------------------------------
  146. void _send(const char * message) {
  147. #if SENSOR_DEBUG
  148. DEBUG_MSG("[SONOFFSC] Sending: %s\n", message);
  149. #endif
  150. Serial.write(message);
  151. Serial.write(SONOFFSC_TERMINATION_CHAR);
  152. }
  153. void _sendConfig() {
  154. char buffer[64];
  155. snprintf(buffer, sizeof(buffer),
  156. "AT+DEVCONFIG=\"uploadFreq\":%lu,\"humiThreshold\":%d,\"tempThreshold\":%d",
  157. _upload_frequency, _hum_threshold, _tmp_threshold
  158. );
  159. _send(buffer);
  160. }
  161. void _parse() {
  162. #if SENSOR_DEBUG
  163. DEBUG_MSG("[SONOFFSC] Received: %s\n", _buffer);
  164. #endif
  165. if (strncmp(_buffer, "AT+UPDATE=", 10) == 0) {
  166. String haystack = String(_buffer);
  167. double value;
  168. unsigned char param_count = 0;
  169. // -------------------------------------------------------------
  170. if (_find(haystack, "humidity", value)) {
  171. _humidity = value;
  172. param_count++;
  173. }
  174. if (_find(haystack, "temperature", value)) {
  175. _temperature = value;
  176. param_count++;
  177. }
  178. if (_find(haystack, "noise", value)) {
  179. _noise_rel = value * 10.0;
  180. param_count++;
  181. }
  182. #if SONOFFSC_HAS_LIGHT_REL
  183. if (_find(haystack, "light", value)) {
  184. _light_rel = constrain(10 - value, 0, 10) * 10.0;
  185. param_count++;
  186. }
  187. #endif
  188. #if SONOFFSC_HAS_DUST_REL
  189. if (_find(haystack, "dusty", value)) {
  190. _dust_rel = value * 10.0;
  191. param_count++;
  192. }
  193. #endif
  194. #if SONOFFSC_HAS_LUX
  195. if (_find(haystack, "illuminance", value)) {
  196. _lux = value;
  197. param_count++;
  198. }
  199. #endif
  200. #if SONOFFSC_HAS_DUST
  201. if (_find(haystack, "dust", value)) {
  202. _dust = value;
  203. param_count++;
  204. }
  205. #endif
  206. #if SONOFFSC_HAS_MOVEMENT
  207. if (_find(haystack, "movement", value)) {
  208. _movement = value;
  209. param_count++;
  210. }
  211. #endif
  212. #if SONOFFSC_HAS_CLAP
  213. if (_find(haystack, "clap", value)) {
  214. _clap = value;
  215. if (_callback) _callback(MAGNITUDE_EVENT, _clap);
  216. param_count++;
  217. }
  218. #endif
  219. // -------------------------------------------------------------
  220. if (param_count > 0) {
  221. _send("AT+SEND=ok");
  222. } else {
  223. _send("AT+SEND=fail");
  224. }
  225. } else if (strncmp(_buffer, "AT+STATUS?", 10) == 0) {
  226. _send("AT+STATUS=4");
  227. }
  228. }
  229. bool _find(String haystack, const char * key, double &value) {
  230. String k = String("\"") + String(key) + String("\"");
  231. unsigned char pos = haystack.indexOf(k);
  232. if (pos >= 0) {
  233. unsigned char ch = haystack.charAt(pos+strlen(key)+3);
  234. if ((ch >= '0' && ch <= '9') || (ch == '-')) {
  235. value = haystack.substring(pos+strlen(key)+3).toFloat();
  236. return true;
  237. }
  238. }
  239. return false;
  240. }
  241. // ---------------------------------------------------------------------
  242. char _buffer[128];
  243. unsigned char _index = 0;
  244. bool _ready_to_parse = false;
  245. double _humidity = 0;
  246. double _temperature = 0;
  247. unsigned char _noise_rel = 0;
  248. #if SONOFFSC_HAS_LIGHT_REL
  249. unsigned char _light_rel = 0;
  250. #endif
  251. #if SONOFFSC_HAS_DUST_REL
  252. unsigned char _dust_rel = 0;
  253. #endif
  254. #if SONOFFSC_HAS_MOVEMENT
  255. unsigned char _movement = 0;
  256. #endif
  257. #if SONOFFSC_HAS_LUX
  258. double _lux = 0;
  259. #endif
  260. #if SONOFFSC_HAS_DUST
  261. double _dust = 0;
  262. #endif
  263. #if SONOFFSC_HAS_CLAP
  264. unsigned char _clap = 0;
  265. #endif
  266. unsigned long _upload_frequency = 60;
  267. unsigned long _hum_threshold = 2;
  268. unsigned long _tmp_threshold = 1;
  269. };
  270. #endif // SENSOR_SUPPORT && SONOFFSC_SUPPORT