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.

333 lines
11 KiB

7 years ago
  1. // -----------------------------------------------------------------------------
  2. // PMSX003 Dust Sensor
  3. // Uses SoftwareSerial library
  4. // Contribution by Òscar Rovira López
  5. // -----------------------------------------------------------------------------
  6. #if SENSOR_SUPPORT && PMSX003_SUPPORT
  7. #pragma once
  8. #include "Arduino.h"
  9. #include "BaseSensor.h"
  10. #include <PMS.h>
  11. #include <SoftwareSerial.h>
  12. //
  13. #define PMS_TYPE_X003 0
  14. #define PMS_TYPE_X003_9 1
  15. #define PMS_TYPE_5003T 2
  16. #define PMS_TYPE_5003ST 3
  17. #ifndef PMS_TYPE
  18. #define PMS_TYPE PMS_TYPE_X003
  19. #endif
  20. // You can enable smart sleep (read 6-times then sleep on 24-reading-cycles) to extend PMS sensor's life.
  21. // Otherwise the default lifetime of PMS sensor is about 8000-hours/1-years.
  22. // The PMS's fan will stop working on sleeping cycle, and will wake up on reading cycle.
  23. #ifndef PMS_SMART_SLEEP
  24. #define PMS_SMART_SLEEP 0
  25. #endif
  26. // [MAGIC][LEN][DATA9|13|17][SUM]
  27. #if PMS_TYPE == PMS_TYPE_5003ST
  28. #define PMS_TYPE_NAME "PMS5003ST"
  29. #define PMS_DATA_COUNT 17
  30. #define PMS_SLOT_COUNT 4
  31. #define PMS_SLOT_NAMES {"PM2.5", "TEMP", "HUMI", "HCHO"}
  32. #define PMS_SLOT_TYPES {MAGNITUDE_PM2dot5, MAGNITUDE_TEMPERATURE, MAGNITUDE_HUMIDITY, MAGNITUDE_ANALOG}
  33. #elif PMS_TYPE == PMS_TYPE_5003T
  34. #define PMS_TYPE_NAME "PMS5003T"
  35. #define PMS_DATA_COUNT 13
  36. #define PMS_SLOT_COUNT 3
  37. #define PMS_SLOT_NAMES {"PM2.5", "TEMP", "HUMI"}
  38. #define PMS_SLOT_TYPES {MAGNITUDE_PM2dot5, MAGNITUDE_TEMPERATURE, MAGNITUDE_HUMIDITY}
  39. #elif PMS_TYPE == PMS_TYPE_X003_9
  40. #define PMS_TYPE_NAME "PMSX003_9"
  41. #define PMS_DATA_COUNT 9
  42. #define PMS_SLOT_COUNT 3
  43. #define PMS_SLOT_NAMES {"PM1.0", "PM2.5", "PM10"}
  44. #define PMS_SLOT_TYPES {MAGNITUDE_PM1dot0, MAGNITUDE_PM2dot5, MAGNITUDE_PM10}
  45. #else
  46. #define PMS_TYPE_NAME "PMSX003"
  47. #define PMS_DATA_COUNT 13
  48. #define PMS_SLOT_COUNT 3
  49. #define PMS_SLOT_NAMES {"PM1.0", "PM2.5", "PM10"}
  50. #define PMS_SLOT_TYPES {MAGNITUDE_PM1dot0, MAGNITUDE_PM2dot5, MAGNITUDE_PM10}
  51. #endif
  52. #define PMS_PACKET_SIZE ((PMS_DATA_COUNT + 3) * 2)
  53. #define PMS_PAYLOAD_SIZE (PMS_DATA_COUNT * 2 + 2)
  54. // PMSX003 sensor utils
  55. // Command functions copied from: https://github.com/fu-hsi/PMS/blob/master/src/PMS.cpp
  56. // Reading function is rewrited to support flexible reading for PMS5003T/PMS5003ST
  57. class PMSX003 {
  58. protected:
  59. SoftwareSerial *_serial = NULL; // Should initialized by child class
  60. public:
  61. // Standby mode. For low power consumption and prolong the life of the sensor.
  62. inline void sleep() {
  63. uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73 };
  64. _serial->write(command, sizeof(command));
  65. }
  66. // Operating mode. Stable data should be got at least 30 seconds after the sensor wakeup from the sleep mode because of the fan's performance.
  67. inline void wakeUp() {
  68. uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74 };
  69. _serial->write(command, sizeof(command));
  70. }
  71. // Active mode. Default mode after power up. In this mode sensor would send serial data to the host automatically.
  72. inline void activeMode() {
  73. uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 };
  74. _serial->write(command, sizeof(command));
  75. }
  76. // Passive mode. In this mode, sensor would send serial data to the host only for request.
  77. inline void passiveMode() {
  78. uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70 };
  79. _serial->write(command, sizeof(command));
  80. }
  81. // Request read, ONLY needed in Passive Mode!!
  82. inline void requestRead() {
  83. uint8_t command[] = { 0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71 };
  84. _serial->write(command, sizeof(command));
  85. }
  86. // Read sensor's data
  87. bool readData(uint16_t data[PMS_DATA_COUNT]) {
  88. do
  89. {
  90. int avail = _serial->available();
  91. #if SENSOR_DEBUG
  92. //debugSend("[SENSOR] %s: Packet available = %d\n", PMS_TYPE_NAME, avail);
  93. #endif
  94. if (avail < PMS_PACKET_SIZE)
  95. break;
  96. if (_serial->read() == 0x42 && _serial->read() == 0x4D)
  97. {
  98. uint16_t sum = 0x42 + 0x4D;
  99. uint16_t size = read16(sum);
  100. #if SENSOR_DEBUG
  101. debugSend("[SENSOR] %s: Payload size = %d\n", PMS_TYPE_NAME, size);
  102. #endif
  103. if (size != PMS_PAYLOAD_SIZE)
  104. {
  105. #if SENSOR_DEBUG
  106. debugSend(("[SENSOR] %s: Payload size != %d \n"), PMS_TYPE_NAME, PMS_PAYLOAD_SIZE);
  107. #endif
  108. break;
  109. }
  110. for (int i = 0; i < PMS_DATA_COUNT; i++)
  111. {
  112. data[i] = read16(sum);
  113. #if SENSOR_DEBUG
  114. //debugSend(("[SENSOR] %s: data[%d] = %d\n"), PMS_TYPE_NAME, i, data[i]);
  115. #endif
  116. }
  117. uint16_t checksum = read16();
  118. #if SENSOR_DEBUG
  119. debugSend(("[SENSOR] %s: Sum=%04X, Checksum=%04X\n"), PMS_TYPE_NAME, sum, checksum);
  120. #endif
  121. if (sum == checksum)
  122. {
  123. return true;
  124. }
  125. break;
  126. }
  127. }
  128. while (true);
  129. return false;
  130. }
  131. private:
  132. // Read 16-bit
  133. inline uint16_t read16() {
  134. return ((uint16_t) _serial->read()) << 8 | _serial->read();
  135. }
  136. // Read 16-bit and calculate checksum
  137. uint16_t read16(uint16_t &checksum) {
  138. uint8_t high = _serial->read();
  139. uint8_t low = _serial->read();
  140. checksum += high;
  141. checksum += low;
  142. return ((uint16_t) high) << 8 | low;
  143. }
  144. };
  145. class PMSX003Sensor : public BaseSensor, PMSX003 {
  146. public:
  147. // ---------------------------------------------------------------------
  148. // Public
  149. // ---------------------------------------------------------------------
  150. PMSX003Sensor(): BaseSensor() {
  151. _count = PMS_SLOT_COUNT;
  152. _sensor_id = SENSOR_PMSX003_ID;
  153. }
  154. ~PMSX003Sensor() {
  155. if (_serial) delete _serial;
  156. }
  157. void setRX(unsigned char pin_rx) {
  158. if (_pin_rx == pin_rx) return;
  159. _pin_rx = pin_rx;
  160. _dirty = true;
  161. }
  162. void setTX(unsigned char pin_tx) {
  163. if (_pin_tx == pin_tx) return;
  164. _pin_tx = pin_tx;
  165. _dirty = true;
  166. }
  167. // ---------------------------------------------------------------------
  168. unsigned char getRX() {
  169. return _pin_rx;
  170. }
  171. unsigned char getTX() {
  172. return _pin_tx;
  173. }
  174. // ---------------------------------------------------------------------
  175. // Sensor API
  176. // ---------------------------------------------------------------------
  177. // Initialization method, must be idempotent
  178. void begin() {
  179. if (!_dirty) return;
  180. if (_serial) delete _serial;
  181. _serial = new SoftwareSerial(_pin_rx, _pin_tx, false, 64);
  182. _serial->enableIntTx(false);
  183. _serial->begin(9600);
  184. passiveMode();
  185. _startTime = millis();
  186. _ready = true;
  187. _dirty = false;
  188. }
  189. // Descriptive name of the sensor
  190. String description() {
  191. char buffer[28];
  192. snprintf(buffer, sizeof(buffer), "%s @ SwSerial(%u,%u)", PMS_TYPE_NAME, _pin_rx, _pin_tx);
  193. return String(buffer);
  194. }
  195. // Descriptive name of the slot # index
  196. String slot(unsigned char index) {
  197. char buffer[36] = {0};
  198. const static char *_slot_names[] = PMS_SLOT_NAMES;
  199. snprintf(buffer, sizeof(buffer), "%s @ %s @ SwSerial(%u,%u)", _slot_names[index], PMS_TYPE_NAME, _pin_rx, _pin_tx);
  200. return String(buffer);
  201. }
  202. // Address of the sensor (it could be the GPIO or I2C address)
  203. String address(unsigned char index) {
  204. char buffer[6];
  205. snprintf(buffer, sizeof(buffer), "%u:%u", _pin_rx, _pin_tx);
  206. return String(buffer);
  207. }
  208. // Type for slot # index
  209. unsigned char type(unsigned char index) {
  210. const static unsigned char _slot_types[] = PMS_SLOT_TYPES;
  211. return _slot_types[index];
  212. }
  213. void pre() {
  214. if (millis() - _startTime < 30000) {
  215. _error = SENSOR_ERROR_WARM_UP;
  216. return;
  217. }
  218. _error = SENSOR_ERROR_OK;
  219. #if PMS_SMART_SLEEP
  220. unsigned int readCycle;
  221. if (_readCount++ > 30) {
  222. readCycle = _readCount % 30;
  223. if (readCycle == 0) {
  224. #if SENSOR_DEBUG
  225. debugSend("[SENSOR] %s: Wake up: %d\n", PMS_TYPE_NAME, _readCount);
  226. #endif
  227. wakeUp();
  228. return;
  229. } else if (readCycle == 1) {
  230. requestRead();
  231. } else if (readCycle > 6) {
  232. return;
  233. }
  234. } else {
  235. readCycle = -1;
  236. }
  237. #endif
  238. uint16_t data[PMS_DATA_COUNT];
  239. if (readData(data)) {
  240. #if PMS_TYPE == PMS_TYPE_5003ST
  241. _slot_values[0] = data[4];
  242. _slot_values[1] = (double)data[13] / 10;
  243. _slot_values[2] = (double)data[14] / 10;
  244. _slot_values[3] = (double)data[12] / 1000;
  245. #elif PMS_TYPE == PMS_TYPE_5003T
  246. _slot_values[0] = data[4];
  247. _slot_values[1] = (double)data[10] / 10;
  248. _slot_values[2] = (double)data[11] / 10;
  249. #else
  250. _slot_values[0] = data[3];
  251. _slot_values[1] = data[4];
  252. _slot_values[2] = data[5];
  253. #endif
  254. }
  255. #if PMS_SMART_SLEEP
  256. if (readCycle == 6) {
  257. sleep();
  258. #if SENSOR_DEBUG
  259. debugSend("[SENSOR] %s: Enter sleep mode: %d\n", PMS_TYPE_NAME, _readCount);
  260. #endif
  261. return;
  262. }
  263. #endif
  264. requestRead();
  265. }
  266. // Current value for slot # index
  267. double value(unsigned char index) {
  268. return _slot_values[index];
  269. }
  270. protected:
  271. unsigned int _pin_rx;
  272. unsigned int _pin_tx;
  273. unsigned long _startTime;
  274. double _slot_values[PMS_SLOT_COUNT] = {0};
  275. #if PMS_SMART_SLEEP
  276. unsigned int _readCount = 0;
  277. #endif
  278. };
  279. #endif // SENSOR_SUPPORT && PMSX003_SUPPORT