diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index fb5cc0c0..b5478d6d 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -34,3 +34,4 @@ add_subdirectory(encoder) add_subdirectory(motor) add_subdirectory(vl53l5cx) add_subdirectory(pcf85063a) +add_subdirectory(pms5003) diff --git a/drivers/pms5003/CMakeLists.txt b/drivers/pms5003/CMakeLists.txt new file mode 100644 index 00000000..9326eaca --- /dev/null +++ b/drivers/pms5003/CMakeLists.txt @@ -0,0 +1 @@ +include(pms5003.cmake) \ No newline at end of file diff --git a/drivers/pms5003/pms5003.cmake b/drivers/pms5003/pms5003.cmake new file mode 100644 index 00000000..3593ea6b --- /dev/null +++ b/drivers/pms5003/pms5003.cmake @@ -0,0 +1,10 @@ +set(DRIVER_NAME pms5003) +add_library(${DRIVER_NAME} INTERFACE) + +target_sources(${DRIVER_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp) + +target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_uart hardware_gpio) diff --git a/drivers/pms5003/pms5003.cpp b/drivers/pms5003/pms5003.cpp new file mode 100644 index 00000000..e69de29b diff --git a/drivers/pms5003/pms5003.hpp b/drivers/pms5003/pms5003.hpp new file mode 100644 index 00000000..f5f07fb3 --- /dev/null +++ b/drivers/pms5003/pms5003.hpp @@ -0,0 +1,114 @@ +#include "pico/stdlib.h" +#include "hardware/uart.h" +#include + +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; + + +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); + 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(); + }; + ~PMS5003() {}; + + void reset() { + sleep_ms(100); + gpio_put(pin_reset, false); + reset_input_buffer(); + sleep_ms(100); + gpio_put(pin_reset, true); + }; + + bool read(response_data &data) { + reset_input_buffer(); + + // Read the 32 byte transaction - SOF + Size + Data + CRC + uart_read_blocking(uart, buffer, 32); + + // 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: + uart_inst_t *uart; + uint pin_tx; + uint pin_rx; + uint pin_reset; + uint pin_enable; + + uint8_t buffer[64]; + + void reset_input_buffer() { + while(uart_is_readable(uart)) { + uart_getc(uart); + } + }; +}; \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ad727ad7..8e45b4d8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -22,6 +22,7 @@ add_subdirectory(breakout_bh1745) add_subdirectory(breakout_icp10125) add_subdirectory(breakout_scd41) add_subdirectory(breakout_vl53l5cx) +add_subdirectory(breakout_pms5003) add_subdirectory(pico_display) add_subdirectory(pico_display_2) diff --git a/examples/breakout_pms5003/CMakeLists.txt b/examples/breakout_pms5003/CMakeLists.txt new file mode 100644 index 00000000..7b63b787 --- /dev/null +++ b/examples/breakout_pms5003/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME pms5003_demo) + +add_executable( + ${OUTPUT_NAME} + pms5003_demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib pms5003) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_pms5003/pms5003_demo.cpp b/examples/breakout_pms5003/pms5003_demo.cpp new file mode 100644 index 00000000..ac18b178 --- /dev/null +++ b/examples/breakout_pms5003/pms5003_demo.cpp @@ -0,0 +1,40 @@ +#include +#include "pico/stdlib.h" +#include "common/pimoroni_common.hpp" +#include "hardware/uart.h" + +#include + +#include "pms5003.hpp" + +using namespace pimoroni; + + +PMS5003 pms5003(uart1, 8, 9, 2, 3); +PMS5003::response_data data; + +int main() { + stdio_init_all(); + + while(true){ + bool result = pms5003.read(data); + if(result){ + printf("%04x ", data.pm_1_0); // PM1.0 ug/m3 (ultrafine particles) + printf("%04x ", data.pm_2_5); // PM2.5 ug/m3 (combustion particles, organic compounds, metals) + printf("%04x ", data.pm_10); // PM10 ug/m3 (dust, pollen, mould spores) + printf("%04x ", data.pm_1_0_ao); // PM 1.0 under atmospheric environment + printf("%04x ", data.pm_2_5_ao); // PM 2.5 under atmospheric environment + printf("%04x ", data.pm_10_ao); // PM 10 under atmospheric environment + printf("%04x ", data.pm_0_3_1l); // PM 0.3 in 0.1L of air + printf("%04x ", data.pm_0_5_1l); // PM 0.5 in 0.1L of air + printf("%04x ", data.pm_1_0_1l); // PM 1.0 in 0.1L of air + printf("%04x ", data.pm_2_5_1l); // PM 2.5 in 0.1L of air + printf("%04x ", data.pm_5_1l); // PM 5 in 0.1L of air + printf("%04x ", data.pm_10_1l); // PM 10 in 0.1L of air + printf("\n"); + } + sleep_ms(100); + }; + + return 0; +} \ No newline at end of file