本文章介紹在Raspberry Pi Pico開發板上連接Sharp GP2Y1014AU0F Dust Sensor與AHT10 溫濕度感應器。即時監控環境中的空氣灰塵濃度與溫濕度。
一、TFT UI:
本文章TFT的UI部份請參閱:
Raspberry Pi Pico (c-sdk)] LVGL Graphics Library & Pico PIO TFT display driver(Serial or Parallel)
二、AHT10 driver library:
根據AHT10 Technical Manual:
- device address: 0x38
三、SHARP GP2Y1014AU0F PM2.5灰塵粉塵感測器:
- Pin 1 ILED vcc的接線:220uF and 150歐姆電阻)
四、成果影片:
五、程式碼:
- pico_lvgl library請參閱:
Raspberry Pi Pico (c-sdk)] LVGL Graphics Library & Pico PIO TFT display driver(Serial or Parallel)
AHT10 libarary:
- CMakeLists.txt
add_library(aht10 INTERFACE) target_sources(aht10 INTERFACE ${CMAKE_CURRENT_LIST_DIR}/aht10.c ) target_include_directories(aht10 INTERFACE ${CMAKE_CURRENT_LIST_DIR} ) target_link_libraries(aht10 INTERFACE hardware_i2c )
- aht10.c
#include "pico/stdio.h" #include "pico/stdlib.h" #include "aht10.h" static i2c_inst_t *aht10_port = i2c0; static uint8_t aht10_i2c_sda; static uint8_t aht10_i2c_scl; void aht10_init(i2c_inst_t *i2c_port, uint8_t sda, uint8_t scl) { aht10_port = i2c_port; aht10_i2c_sda = sda; aht10_i2c_scl = scl; // I2C Initialisation. Using it at 400Khz. i2c_init(aht10_port, 400*1000); gpio_set_function(aht10_i2c_sda, GPIO_FUNC_I2C); gpio_set_function(aht10_i2c_scl, GPIO_FUNC_I2C); gpio_pull_up(aht10_i2c_sda); gpio_pull_up(aht10_i2c_scl); uint8_t cmd[3]; cmd[0] = AHT10_INIT_CMD; cmd[1] = 0x08; cmd[2] = 0x00; i2c_write_blocking(aht10_port, AHT10_I2C_ADDR, cmd, 3, false); sleep_ms(300); } bool aht10_trigger_measurement(float* humidity, float *temperature) { static uint8_t cmd[3] = {AHT10_TRIG_CMD, 0x33, 0x00}; uint8_t data_buff[10]; i2c_write_blocking(aht10_port, AHT10_I2C_ADDR, cmd, 3, false); sleep_ms(80); i2c_read_blocking(aht10_port, AHT10_I2C_ADDR, data_buff, 6, false); if (data_buff[0] >> 7) { // status bit[7] --> 1: The device is busy, *humidity = 0; *temperature = 0; return false; } //Convert the temperature as described in the data book *humidity = (data_buff[1] << 12) | ( data_buff[2] << 4) | ((data_buff[3] & 0xF0) >> 4); *humidity = (*humidity/(1 << 20)) * 100.0; *temperature = ((data_buff[3] & 0xf) << 16) | ( data_buff[4] << 8) | data_buff[5]; *temperature = (*temperature * 200.0 / (1 << 20)) - 50; return true; } void aht10_soft_reset() { static uint8_t cmd[1] = {AHT10_RESET_CMD}; i2c_write_blocking(aht10_port, AHT10_I2C_ADDR, cmd, 1, false); sleep_ms(80); }
- aht10.h
#ifndef __AHT10_H__ #define __AHT10_H__ #include "hardware/i2c.h" #define AHT10_INIT_CMD 0b11100001 #define AHT10_TRIG_CMD 0b10101100 #define AHT10_RESET_CMD 0b10111010 #define AHT10_I2C_ADDR 0x38 void aht10_init(i2c_inst_t *i2c_port, uint8_t sda, uint8_t scl); bool aht10_trigger_measurement(float* humidity, float *temperature); void aht10_soft_reset(); #endif
- CMakeLists.txt(root)
# Generated Cmake Pico project file cmake_minimum_required(VERSION 3.13) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Initialise pico_sdk from installed location # (note this can come from environment, CMake cache etc) # == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == if(WIN32) set(USERHOME $ENV{USERPROFILE}) else() set(USERHOME $ENV{HOME}) endif() set(sdkVersion 2.1.0) set(toolchainVersion 13_2_Rel1) set(picotoolVersion 2.1.0) set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) if (EXISTS ${picoVscode}) include(${picoVscode}) endif() # ==================================================================================== set(PICO_BOARD pico CACHE STRING "Board type") # Pull in Raspberry Pi Pico SDK (must be before project) include(pico_sdk_import.cmake) project(pico_dust_ht C CXX ASM) # Initialise the Raspberry Pi Pico SDK pico_sdk_init() # Add executable. Default name is the project name, version 0.1 add_executable(pico_dust_ht pico_dust_ht.c ) pico_set_program_name(pico_dust_ht "sharp_gp2y10") pico_set_program_version(pico_dust_ht "0.1") # Modify the below lines to enable/disable output over UART/USB pico_enable_stdio_uart(pico_dust_ht 0) pico_enable_stdio_usb(pico_dust_ht 1) # Add the standard library to the build target_link_libraries(pico_dust_ht pico_stdlib hardware_adc hardware_pio) # Add the standard include files to the build target_include_directories(pico_dust_ht PRIVATE ${CMAKE_CURRENT_LIST_DIR} ) add_subdirectory(aht10) add_subdirectory(pico_lvgl) target_link_libraries(pico_dust_ht aht10 pico_lvgl ) pico_add_extra_outputs(pico_dust_ht)
- pico_dust_ht.c
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/adc.h" #include "hardware/pio.h" #include "aht10.h" #include "pico_lvgl.h" #define GP2Y10_ILED_PIN 18 #define GP2Y10_VOUT_PIN 26 #define AHT10_I2C_PORT i2c0 #define AHT10_SDA_PIN 16 #define AHT10_SCL_PIN 17 PIO TFT_PIO = pio1; #define TFT_SM 1 #define TFT_SDI_GPIO 9 #define TFT_CSX_DCX_SCK_GPIO 6 // CSX=8, DCX=7, SCK=6, SIDE_SET // 12-bit conversion, assume max value == ADC_VREF == 3.3 V const float conversion_factor = 3.3f / (1 << 12); lv_obj_t *humi_label, *temp_label, *ug_label, *dusk_meter, *avg_1h_label, *avg_6h_label, *max_last_hour_label; lv_obj_t *humi_bar, *temp_bar; lv_meter_indicator_t * indic; void lgvl_ui() { dusk_meter = lv_meter_create(lv_scr_act()); lv_obj_align(dusk_meter, LV_ALIGN_TOP_MID, 0, 10); lv_obj_set_size(dusk_meter, 200, 200); /*Add a scale first*/ lv_meter_scale_t * scale = lv_meter_add_scale(dusk_meter); lv_meter_set_scale_ticks(dusk_meter, scale, 70, 2, 5, lv_palette_main(LV_PALETTE_GREY)); lv_meter_set_scale_major_ticks(dusk_meter, scale, 10, 4, 15, lv_color_black(), 10); /*Add a green arc to the start*/ indic = lv_meter_add_arc(dusk_meter, scale, 3, lv_palette_main(LV_PALETTE_GREEN), 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 0); lv_meter_set_indicator_end_value(dusk_meter, indic, 50); /*Make the tick lines green at the start of the scale*/ indic = lv_meter_add_scale_lines(dusk_meter, scale, lv_palette_main(LV_PALETTE_GREEN), lv_palette_main(LV_PALETTE_GREEN), false, 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 0); lv_meter_set_indicator_end_value(dusk_meter, indic, 50); //== /*Add a yellow arc to the start*/ indic = lv_meter_add_arc(dusk_meter, scale, 3, lv_palette_main(LV_PALETTE_YELLOW), 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 51); lv_meter_set_indicator_end_value(dusk_meter, indic, 100); /*Make the tick lines yellow at the start of the scale*/ indic = lv_meter_add_scale_lines(dusk_meter, scale, lv_palette_main(LV_PALETTE_YELLOW), lv_palette_main(LV_PALETTE_YELLOW), false, 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 51); lv_meter_set_indicator_end_value(dusk_meter, indic, 100); //== /*Add a orange arc to the start*/ indic = lv_meter_add_arc(dusk_meter, scale, 3, lv_palette_main(LV_PALETTE_ORANGE), 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 101); lv_meter_set_indicator_end_value(dusk_meter, indic, 150); /*Make the tick lines yellow at the start of the scale*/ indic = lv_meter_add_scale_lines(dusk_meter, scale, lv_palette_main(LV_PALETTE_ORANGE), lv_palette_main(LV_PALETTE_ORANGE), false, 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 101); lv_meter_set_indicator_end_value(dusk_meter, indic, 150); //== /*Add a red arc to the end*/ indic = lv_meter_add_arc(dusk_meter, scale, 3, lv_palette_main(LV_PALETTE_RED), 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 151); lv_meter_set_indicator_end_value(dusk_meter, indic, 200); /*Make the tick lines red at the end of the scale*/ indic = lv_meter_add_scale_lines(dusk_meter, scale, lv_palette_main(LV_PALETTE_RED), lv_palette_main(LV_PALETTE_RED), false, 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 151); lv_meter_set_indicator_end_value(dusk_meter, indic, 200); //== /*Add a purple arc to the start*/ indic = lv_meter_add_arc(dusk_meter, scale, 3, lv_palette_main(LV_PALETTE_PURPLE), 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 201); lv_meter_set_indicator_end_value(dusk_meter, indic, 350); /*Make the tick lines yellow at the start of the scale*/ indic = lv_meter_add_scale_lines(dusk_meter, scale, lv_palette_main(LV_PALETTE_PURPLE), lv_palette_main(LV_PALETTE_PURPLE), false, 0); lv_meter_set_indicator_start_value(dusk_meter, indic, 201); lv_meter_set_indicator_end_value(dusk_meter, indic, 350); /*Add a needle line indicator*/ indic = lv_meter_add_needle_line(dusk_meter, scale, 4, lv_palette_main(LV_PALETTE_GREY), -10); lv_meter_set_scale_range(dusk_meter, scale, 0, 350, 270, 135); // avg_1h_label = lv_label_create(lv_scr_act()); avg_6h_label = lv_label_create(lv_scr_act()); max_last_hour_label = lv_label_create(lv_scr_act()); lv_obj_align(avg_1h_label, LV_ALIGN_TOP_LEFT, 5,190); lv_obj_align(avg_6h_label, LV_ALIGN_TOP_RIGHT, -5,190); lv_obj_align(max_last_hour_label, LV_ALIGN_TOP_LEFT, 5,5); lv_obj_set_style_text_color(avg_1h_label, lv_palette_main(LV_PALETTE_BLUE), 0); lv_obj_set_style_text_color(avg_6h_label, lv_palette_main(LV_PALETTE_BLUE), 0); lv_obj_set_style_text_color(max_last_hour_label, lv_palette_main(LV_PALETTE_RED), 0); lv_label_set_text(avg_1h_label, "Avg(1 Hour)\n-.- ug/m3"); lv_label_set_text(avg_6h_label, "Avg(6 Hours)\n-.- ug/m3"); lv_label_set_text(max_last_hour_label, "Max(Last Hour)\n-.- ug/m3"); humi_bar = lv_bar_create(lv_scr_act()); lv_obj_set_size(humi_bar, 20, 150); lv_obj_align(humi_bar, LV_ALIGN_BOTTOM_LEFT, 80, -40); lv_bar_set_value(humi_bar, 0, LV_ANIM_ON); lv_bar_set_range(humi_bar, 0, 100); temp_bar = lv_bar_create(lv_scr_act()); lv_obj_set_size(temp_bar, 20, 150); lv_obj_align(temp_bar, LV_ALIGN_BOTTOM_RIGHT, -80, -40); lv_bar_set_value(temp_bar, 0, LV_ANIM_ON); lv_bar_set_range(temp_bar, -10, 70); lv_obj_t *tl = lv_label_create(lv_scr_act()); lv_label_set_text(tl, "Humidity"); lv_obj_align_to(tl, humi_bar, LV_ALIGN_OUT_TOP_MID, 0, -10); tl = lv_label_create(lv_scr_act()); lv_label_set_text(tl, "Temperature"); lv_obj_align_to(tl, temp_bar, LV_ALIGN_OUT_TOP_MID, 0, -10); humi_label = lv_label_create(lv_scr_act()); temp_label = lv_label_create(lv_scr_act()); ug_label = lv_label_create(lv_scr_act()); //lv_obj_set_size(humi_label, 100,30); //lv_obj_set_size(humi_label, 50,30); //lv_obj_set_size(temp_label, 70,30); //lv_obj_set_size(ug_label, 100,30); lv_obj_align(ug_label, LV_ALIGN_TOP_MID, 0, 170); lv_obj_align_to(humi_label, humi_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 5); lv_obj_align_to(temp_label, temp_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 5); } int main() { stdio_init_all(); pico_lvgl_tft_init(TFT_PIO, TFT_SM, TFT_SDI_GPIO, TFT_CSX_DCX_SCK_GPIO); //tft_set_orientation(TFT_ORIENTATION_LANDSCAPE); pico_lvgl_display_init(5); lgvl_ui(); gpio_init(GP2Y10_ILED_PIN); gpio_set_dir(GP2Y10_ILED_PIN, true); adc_init(); adc_gpio_init(GP2Y10_VOUT_PIN); adc_select_input(0); aht10_init(AHT10_I2C_PORT, AHT10_SDA_PIN, AHT10_SCL_PIN); uint16_t value, index_1h=0, index_6h=0; float ug, humidity, temperature, ug_avg_six_hours=0, ug_avg_one_hour=0; uint8_t buff[200]; static float one_hour_ug[3600]; static float six_hours_ug[6]; static float max_last_hour = 0; while (true) { // == get sharp gp2y10 ug value == gpio_put(GP2Y10_ILED_PIN, false); sleep_us(280); value = adc_read(); sleep_us(40); gpio_put(GP2Y10_ILED_PIN, true); sleep_us(9680); ug = (((0.172 * value*conversion_factor) - 0.1) * 1000); //(5v) // == get sharp gp2y10 ug value == printf("adcvalue:%d, \tv:%.2f\t ug:.%.2f\n", value, value*conversion_factor,ug); aht10_trigger_measurement(&humidity, &temperature); printf("humidity:%.2f %%, \ttemperature:%.2fC\n\n", humidity, temperature); sprintf(buff, "\t%3.0f ug/m3", ug<0?0:ug); lv_label_set_text(ug_label, buff); sprintf(buff, "%2.1f \xC2\xB0" "C", temperature); lv_label_set_text(temp_label, buff); sprintf(buff, "%2.1f%%", humidity); lv_label_set_text(humi_label, buff); lv_meter_set_indicator_value(dusk_meter, indic, (int)(ug)); lv_bar_set_value(humi_bar, humidity, LV_ANIM_ON); lv_bar_set_value(temp_bar, temperature, LV_ANIM_ON); one_hour_ug[index_1h] = ug; if (++index_1h == 3600) { index_1h = 0; } if ((index_1h) % 60 == 0) { for (int i = 0; i < 3600; i++) { ug_avg_one_hour += one_hour_ug[i]; } ug_avg_one_hour /= 3600; // sprintf(buff, "Avg(1 Hour)\n%3.1f ug/m3", ug_avg_one_hour); lv_label_set_text(avg_1h_label, buff); } if(index_1h == 0) { six_hours_ug[index_6h++] = ug_avg_one_hour; if (index_6h == 6) { index_6h = 0; } for (int i =0; i < 6; i++) { ug_avg_six_hours += six_hours_ug[i]; } ug_avg_six_hours = ug_avg_six_hours / 6; // sprintf(buff, "Avg(6 Hours)\n%3.1f ug/m3", ug_avg_six_hours); lv_label_set_text(avg_6h_label, buff); } max_last_hour = 0; for (int i = 0; i < 3600; i++) { if (one_hour_ug[i] > max_last_hour) max_last_hour = one_hour_ug[i]; } sprintf(buff, "Max(Last Hour)\n%3.1f ug/m3", max_last_hour); lv_label_set_text(max_last_hour_label, buff); lv_timer_handler(); sleep_ms(1000); } }
沒有留言:
張貼留言