| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  | #include "pico/stdlib.h"
 | 
					
						
							|  |  |  | #include "hardware/uart.h"
 | 
					
						
							| 
									
										
										
										
											2022-06-29 07:14:39 +00:00
										 |  |  | #include "common/pimoroni_common.hpp"
 | 
					
						
							|  |  |  | #include "common/pimoroni_i2c.hpp"
 | 
					
						
							| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  | #include <cstring>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | constexpr char PMS5003_SOF[] = "\x42\x4d"; | 
					
						
							|  |  |  | constexpr uint16_t PMS5003_SOFU = 0x424d; | 
					
						
							|  |  |  | constexpr char PMS5003_CMD_MODE_PASSIVE[] = "\xe1\x00\x00"; | 
					
						
							|  |  |  | constexpr char PMS5003_CMD_MODE_ACTIVE[] = "\xe1\x00\x01"; | 
					
						
							|  |  |  | constexpr char PMS5003_CMD_READ[] = "\xe2\x00\x00"; | 
					
						
							|  |  |  | constexpr char PMS5003_CMD_SLEEP[] = "\xe4\x00\x00"; | 
					
						
							|  |  |  | constexpr char PMS5003_CMD_WAKEUP[] = "\xe4\x00\x01"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | constexpr uint PMS5003_MAX_RESET_TIME = 20000; | 
					
						
							|  |  |  | constexpr uint PMS5003_MAX_RESP_TIME = 5000; | 
					
						
							|  |  |  | constexpr uint PMS5003_MIN_CMD_INTERVAL = 100; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 07:14:39 +00:00
										 |  |  | const uint8_t PMSA003I_DEFAULT_I2C_ADDRESS = 0x12; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class PMS5003 { | 
					
						
							|  |  |  |     public: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #pragma pack(push, 1)
 | 
					
						
							|  |  |  |         struct alignas(1) response_data { | 
					
						
							|  |  |  |             uint16_t pm_1_0; | 
					
						
							|  |  |  |             uint16_t pm_2_5; | 
					
						
							|  |  |  |             uint16_t pm_10; | 
					
						
							|  |  |  |             uint16_t pm_1_0_ao; | 
					
						
							|  |  |  |             uint16_t pm_2_5_ao; | 
					
						
							|  |  |  |             uint16_t pm_10_ao; | 
					
						
							|  |  |  |             uint16_t pm_0_3_1l; | 
					
						
							|  |  |  |             uint16_t pm_0_5_1l; | 
					
						
							|  |  |  |             uint16_t pm_1_0_1l; | 
					
						
							|  |  |  |             uint16_t pm_2_5_1l; | 
					
						
							|  |  |  |             uint16_t pm_5_1l; | 
					
						
							|  |  |  |             uint16_t pm_10_1l; | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | #pragma pack(pop)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         PMS5003(uart_inst_t *uart, uint pin_tx, uint pin_rx, | 
					
						
							|  |  |  |                 uint pin_reset, uint pin_enable) | 
					
						
							|  |  |  |                 : uart(uart), | 
					
						
							|  |  |  |                 pin_tx(pin_tx), | 
					
						
							|  |  |  |                 pin_rx(pin_rx), | 
					
						
							|  |  |  |                 pin_reset(pin_reset), | 
					
						
							|  |  |  |                 pin_enable(pin_enable) { | 
					
						
							|  |  |  |                     uart_init(uart, 9600); | 
					
						
							|  |  |  |                     gpio_init(pin_tx);gpio_set_function(pin_tx, GPIO_FUNC_UART); | 
					
						
							|  |  |  |                     gpio_init(pin_rx);gpio_set_function(pin_rx, GPIO_FUNC_UART); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 07:14:39 +00:00
										 |  |  |                     common_init(); | 
					
						
							| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  |                 }; | 
					
						
							| 
									
										
										
										
											2022-06-29 07:14:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         PMS5003(pimoroni::I2C *i2c, uint pin_reset, uint pin_enable) : i2c(i2c), pin_reset(pin_reset), pin_enable(pin_enable) { | 
					
						
							|  |  |  |             common_init(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  |         ~PMS5003() {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         void reset() { | 
					
						
							|  |  |  |             sleep_ms(100); | 
					
						
							|  |  |  |             gpio_put(pin_reset, false); | 
					
						
							|  |  |  |             reset_input_buffer(); | 
					
						
							|  |  |  |             sleep_ms(100); | 
					
						
							|  |  |  |             gpio_put(pin_reset, true); | 
					
						
							| 
									
										
										
										
											2022-06-29 07:14:39 +00:00
										 |  |  |             if(i2c) { | 
					
						
							|  |  |  |                 sleep_ms(1000); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bool read(response_data &data) { | 
					
						
							|  |  |  |             reset_input_buffer(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Read the 32 byte transaction - SOF + Size + Data + CRC
 | 
					
						
							| 
									
										
										
										
											2022-06-29 07:14:39 +00:00
										 |  |  |             if(i2c) { | 
					
						
							|  |  |  |                 i2c->read_blocking(PMSA003I_DEFAULT_I2C_ADDRESS, buffer, 32, false); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 uart_read_blocking(uart, buffer, 32); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // test the checksum matches, if not quit early with a false return value
 | 
					
						
							|  |  |  |             uint16_t checksum = (buffer[30] << 8) | buffer[31]; | 
					
						
							|  |  |  |             uint16_t compare = 0; | 
					
						
							|  |  |  |             for(int i = 0; i < 30; i++) { | 
					
						
							|  |  |  |                 compare += buffer[i]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if(compare != checksum) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             // Copy the data into the result struct
 | 
					
						
							|  |  |  |             memcpy(&data, buffer + 4, sizeof(data)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Byteswap the results
 | 
					
						
							|  |  |  |             data.pm_1_0     = __builtin_bswap16(data.pm_1_0); | 
					
						
							|  |  |  |             data.pm_2_5     = __builtin_bswap16(data.pm_2_5); | 
					
						
							|  |  |  |             data.pm_10      = __builtin_bswap16(data.pm_10); | 
					
						
							|  |  |  |             data.pm_1_0_ao  = __builtin_bswap16(data.pm_1_0_ao); | 
					
						
							|  |  |  |             data.pm_2_5_ao  = __builtin_bswap16(data.pm_2_5_ao); | 
					
						
							|  |  |  |             data.pm_10_ao   = __builtin_bswap16(data.pm_10_ao); | 
					
						
							|  |  |  |             data.pm_0_3_1l  = __builtin_bswap16(data.pm_0_3_1l); | 
					
						
							|  |  |  |             data.pm_0_5_1l  = __builtin_bswap16(data.pm_0_5_1l); | 
					
						
							|  |  |  |             data.pm_1_0_1l  = __builtin_bswap16(data.pm_1_0_1l); | 
					
						
							|  |  |  |             data.pm_2_5_1l  = __builtin_bswap16(data.pm_2_5_1l); | 
					
						
							|  |  |  |             data.pm_5_1l    = __builtin_bswap16(data.pm_5_1l); | 
					
						
							|  |  |  |             data.pm_10_1l   = __builtin_bswap16(data.pm_10_1l); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private: | 
					
						
							| 
									
										
										
										
											2022-06-29 07:14:39 +00:00
										 |  |  |         // I2C mode
 | 
					
						
							|  |  |  |         pimoroni::I2C *i2c = nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // UART mode
 | 
					
						
							|  |  |  |         uart_inst_t *uart = nullptr; | 
					
						
							|  |  |  |         uint pin_tx = pimoroni::PIN_UNUSED; | 
					
						
							|  |  |  |         uint pin_rx = pimoroni::PIN_UNUSED; | 
					
						
							| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  |         uint pin_reset; | 
					
						
							|  |  |  |         uint pin_enable; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         uint8_t buffer[64]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         void reset_input_buffer() { | 
					
						
							| 
									
										
										
										
											2022-06-29 07:14:39 +00:00
										 |  |  |             if(i2c) return; | 
					
						
							| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  |             while(uart_is_readable(uart)) { | 
					
						
							|  |  |  |                 uart_getc(uart); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2022-06-29 07:14:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         void common_init() { | 
					
						
							|  |  |  |             gpio_init(pin_reset);gpio_set_function(pin_reset, GPIO_FUNC_SIO);gpio_set_dir(pin_reset, GPIO_OUT);gpio_put(pin_reset, false); | 
					
						
							|  |  |  |             gpio_init(pin_enable);gpio_set_function(pin_enable, GPIO_FUNC_SIO);gpio_set_dir(pin_enable, GPIO_OUT);gpio_put(pin_enable, true); | 
					
						
							|  |  |  |             reset(); | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2022-05-25 09:48:26 +00:00
										 |  |  | }; |