一、前言
隨著城市人口規(guī)模的不斷擴(kuò)大和城市化進(jìn)程的不斷加速,城市環(huán)境污染問題越來越受到人們的關(guān)注。環(huán)境監(jiān)測是評估環(huán)境污染狀況、制定環(huán)保政策、維護(hù)人民身體健康的重要手段之一。傳統(tǒng)的環(huán)境監(jiān)測手段需要人工參與,成本高、效率低,不能滿足大規(guī)模的實時監(jiān)測需求。
本項目基于STM32設(shè)計的城市環(huán)境監(jiān)測看板,實現(xiàn)對城市環(huán)境噪聲、溫濕度和粉塵濃度的實時監(jiān)測和數(shù)據(jù)采集。 主控芯片采用STM32F103C8T6,具有較高的性能和穩(wěn)定性,能夠有效處理各種傳感器采集的數(shù)據(jù);環(huán)境噪聲傳感器采用ADC輸出模擬信號表示環(huán)境噪聲強(qiáng)度,能夠準(zhǔn)確測量城市環(huán)境噪聲;環(huán)境溫濕度傳感器采用DHT11,具有高精度、快速響應(yīng)和成本低廉等優(yōu)點,能夠準(zhǔn)確測量城市環(huán)境的溫濕度;粉塵濃度模塊采用PM2.5粉塵濃度檢測模塊GP2Y10,能夠?qū)崟r快速地檢測城市空氣中的PM2.5粉塵濃度,為環(huán)境污染控制提供數(shù)據(jù)支持。
在本項目中,通過對不同傳感器和主控芯片的選擇和應(yīng)用,成功設(shè)計了一款城市環(huán)境監(jiān)測看板,能夠?qū)崟r監(jiān)測和記錄城市環(huán)境的噪聲、溫濕度和粉塵濃度等信息,為城市環(huán)保管理部門和公眾提供了有力的數(shù)據(jù)支持。
二、項目整體設(shè)計思路
2.1 硬件設(shè)計思路
(1)主控芯片選擇:本項目選用了STM32F103C8T6作為主控芯片,該芯片具有高性能、低功耗和豐富的外設(shè)資源,可以滿足環(huán)境監(jiān)測系統(tǒng)的要求。
(2)傳感器選擇:環(huán)境噪聲傳感器采用ADC輸出模擬信號的方式來表示環(huán)境噪聲強(qiáng)度,這里可以選擇常見的電容麥克風(fēng)或者MEMS麥克風(fēng)作為傳感器。環(huán)境溫濕度傳感器采用DHT11,它是一種數(shù)字式溫濕度傳感器,具有成本低廉、響應(yīng)速度快的特點。粉塵濃度模塊采用GP2Y10,它是一種激光散射式粉塵傳感器,能夠?qū)崟r檢測空氣中的PM2.5粉塵濃度。
(3)連接方式:將傳感器與主控芯片連接,可以通過使用模擬輸入通道連接環(huán)境噪聲傳感器的輸出,通過GPIO口連接DHT11傳感器和GP2Y10傳感器。
(4)電源設(shè)計:根據(jù)系統(tǒng)需求選擇合適的電源模塊,保證系統(tǒng)的穩(wěn)定供電。
2.2 軟件設(shè)計思路
(1)系統(tǒng)初始化:在程序開始時進(jìn)行系統(tǒng)的初始化,包括GPIO口初始化、ADC模塊初始化等。
(2)傳感器數(shù)據(jù)采集:通過主控芯片的GPIO口或者ADC模塊采集環(huán)境噪聲、溫濕度和粉塵濃度傳感器的數(shù)據(jù)。對于環(huán)境噪聲傳感器,利用ADC模塊將模擬信號轉(zhuǎn)換為數(shù)字量;對于DHT11和GP2Y10傳感器,直接讀取其數(shù)字輸出。
(3)數(shù)據(jù)處理與顯示:將采集到的傳感器數(shù)據(jù)進(jìn)行處理,并通過LCD顯示屏或者其他輸出方式實時展示結(jié)果??梢栽O(shè)計相應(yīng)的算法進(jìn)行數(shù)據(jù)濾波、校正或者轉(zhuǎn)換為可讀格式。同時,根據(jù)不同的數(shù)據(jù)范圍,可以設(shè)置合適的閾值來判斷環(huán)境的狀態(tài)是否達(dá)到預(yù)警水平。
(4)網(wǎng)絡(luò)通信:通過網(wǎng)絡(luò)模塊(ESP8266)將數(shù)據(jù)傳輸到遠(yuǎn)程監(jiān)控終端和云平臺,實現(xiàn)遠(yuǎn)程監(jiān)控和數(shù)據(jù)存儲??梢允褂肏TTP、MQTT等協(xié)議進(jìn)行數(shù)據(jù)傳輸,保證數(shù)據(jù)的安全性和可靠性。
(5)系統(tǒng)控制:設(shè)計合適的用戶界面和控制方式,使用戶能夠方便地操作本項目??梢酝ㄟ^按鍵、觸摸屏或者無線遙控等方式進(jìn)行系統(tǒng)的開關(guān)、參數(shù)調(diào)節(jié)等操作。
三、硬件連線
(1)環(huán)境噪聲傳感器(模擬信號輸出):
將傳感器的模擬輸出連接到STM32的一個ADC輸入通道(PA0)。
(2)環(huán)境溫濕度傳感器(DHT11):
將DHT11的VCC引腳連接到STM32的3.3V電源。
將DHT11的GND引腳連接到STM32的GND引腳。
將DHT11的DATA引腳連接到STM32的一個GPIO輸入引腳(PB0)。
(3)粉塵濃度模塊(GP2Y10):
將GP2Y10的VCC引腳連接到STM32的3.3V電源。
將GP2Y10的GND引腳連接到STM32的GND引腳。
將GP2Y10的Vo引腳連接到STM32的一個ADC輸入通道(PA1)。
(4)OLED顯示屏(0.96寸OLED):
將OLED顯示屏的SDA引腳連接到STM32的I2C總線的SDA引腳(PB7)。
將OLED顯示屏的SCL引腳連接到STM32的I2C總線的SCL引腳(PB6)。
將OLED顯示屏的VCC引腳連接到STM32的3.3V電源。
將OLED顯示屏的GND引腳連接到STM32的GND引腳。
四、項目代碼設(shè)計
4.1 主核心代碼
// 初始化GPIO和ADC模塊
// 啟用I2C總線
// 初始化OLED顯示屏驅(qū)動程序
// 循環(huán)讀取傳感器數(shù)據(jù)并顯示
while(1) {
// 讀取環(huán)境噪聲傳感器的模擬信號并轉(zhuǎn)換為數(shù)字值
int noise = read_adc();
// 通過GPIO讀取DHT11傳感器的溫濕度數(shù)據(jù)
float temperature, humidity;
read_DHT11(&temperature, &humidity);
// 讀取粉塵濃度的模擬電壓信號并轉(zhuǎn)換為PM2.5顆粒物濃度值
float dust_level = read_GP2Y10();
// 將讀取的數(shù)據(jù)顯示在OLED屏幕上
oled_clear();
oled_print_string("Noise: " + noise + "dB");
oled_print_string("Temp: " + temperature + "C");
oled_print_string("Humidity: " + humidity + "%");
oled_print_string("Dust Level: " + dust_level + "ug/m^3");
// 根據(jù)需要設(shè)置警報閾值并發(fā)出警報
if (noise > 70 || temperature > 30 || dust_level > 50) {
beep_alarm();
}
// 等待一段時間再進(jìn)行下一次循環(huán)
delay_ms(1000);
}
4.2 模塊子函數(shù)代碼
#include "stm32f1xx_hal.h"
#include "ssd1306.h"
void GPIO_Init(void) {
// 初始化GPIO引腳
// 設(shè)置環(huán)境噪聲傳感器的模擬輸入引腳為模擬輸入模式
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 使能GPIOA時鐘
GPIOA->CRL &= ~GPIO_CRL_MODE0; // 復(fù)位模式寄存器中的MODE0位
GPIOA->CRL &= ~GPIO_CRL_CNF0; // 復(fù)位配置寄存器中的CNF0位
// 設(shè)置DHT11的DATA引腳為上拉輸入模式
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // 使能GPIOB時鐘
GPIOB->CRL &= ~GPIO_CRL_MODE0; // 復(fù)位模式寄存器中的MODE0位
GPIOB->CRL &= ~GPIO_CRL_CNF0_0; // 復(fù)位配置寄存器中的CNF0位
GPIOB->CRL |= GPIO_CRL_CNF0_1; // 設(shè)置為上拉輸入模式
// 初始化其他GPIO引腳,例如GP2Y10的Vo引腳和I2C總線的引腳等
}
void ADC_Init(void) {
// 初始化ADC模塊
// 使能ADC1時鐘
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// 配置ADC通道和采樣時間
ADC1->SMPR2 |= ADC_SMPR2_SMP0_2; // 設(shè)置ADC通道為第0個通道,并設(shè)置采樣時間為28.5個周期
// 配置ADC轉(zhuǎn)換模式為單次轉(zhuǎn)換模式
ADC1->CR2 &= ~ADC_CR2_CONT; // 關(guān)閉連續(xù)轉(zhuǎn)換模式,使用單次轉(zhuǎn)換模式
// 配置ADC轉(zhuǎn)換觸發(fā)源為軟件觸發(fā)
ADC1->CR2 &= ~ADC_CR2_EXTSEL; // 清除外部觸發(fā)選擇位
ADC1->CR2 |= ADC_CR2_EXTSEL_0; // 設(shè)置為軟件觸發(fā)模式
// 使能ADC模塊
ADC1->CR2 |= ADC_CR2_ADON;
}
void I2C_Init(void) {
// 初始化I2C總線
// 使能I2C1時鐘
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// 配置I2C總線時鐘頻率
I2C1->CR2 |= 36; // 根據(jù)系統(tǒng)時鐘頻率和所需的I2C總線速率進(jìn)行配置
// 配置I2C總線模式和器件地址
I2C1->CR1 &= ~I2C_CR1_SMBUS; // 關(guān)閉SMBus模式
I2C1->CR1 |= I2C_CR1_PE; // 使能I2C總線
// 配置I2C總線的GPIO引腳
// 設(shè)置SCL引腳為開漏輸出模式
GPIOB->CRH |= GPIO_CRH_MODE6_0;
GPIOB->CRH &= ~GPIO_CRH_MODE6_1;
GPIOB->CRH |= GPIO_CRH_CNF6_0;
GPIOB->CRH |= GPIO_CRH_CNF6_1;
// 設(shè)置SDA引腳為開漏輸出模式
GPIOB->CRH |= GPIO_CRH_MODE7_0;
GPIOB->CRH &= ~GPIO_CRH_MODE7_1;
GPIOB->CRH |= GPIO_CRH_CNF7_0;
GPIOB->CRH |= GPIO_CRH_CNF7_1;
}
void OLED_Init(void) {
// 初始化OLED顯示屏驅(qū)動程序
ssd1306_Init(); // 使用 SSD1306 驅(qū)動程序進(jìn)行初始化
}
void System_Init(void) {
// 初始化系統(tǒng)組件
HAL_Init(); // 使用HAL庫進(jìn)行初始化,如果沒有使用HAL庫,可以根據(jù)芯片廠商提供的庫進(jìn)行初始化
GPIO_Init(); // 初始化GPIO
ADC_Init(); // 初始化ADC模塊
I2C_Init(); // 初始化I2C總線
OLED_Init(); // 初始化OLED顯示屏驅(qū)動程序
}
4.3 OLED驅(qū)動代碼
#include "stm32f10x.h"
#include "OLED.h"
#define OLED_CS_GPIO GPIOB
#define OLED_CS_PIN GPIO_Pin_12
#define OLED_DC_GPIO GPIOB
#define OLED_DC_PIN GPIO_Pin_13
#define OLED_RST_GPIO GPIOB
#define OLED_RST_PIN GPIO_Pin_14
void Delay_ms(uint16_t time) {
while(time--) {
uint16_t i = 12000; // 延時大概1ms
while(i--);
}
}
void GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// CS引腳配置
GPIO_InitStruct.GPIO_Pin = OLED_CS_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(OLED_CS_GPIO, &GPIO_InitStruct);
// DC引腳配置
GPIO_InitStruct.GPIO_Pin = OLED_DC_PIN;
GPIO_Init(OLED_DC_GPIO, &GPIO_InitStruct);
// RST引腳配置
GPIO_InitStruct.GPIO_Pin = OLED_RST_PIN;
GPIO_Init(OLED_RST_GPIO, &GPIO_InitStruct);
}
void SPI_Init(void) {
SPI_InitTypeDef SPI_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// SPI總線配置
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);
}
void OLED_WriteCmd(uint8_t cmd) {
GPIO_ResetBits(OLED_CS_GPIO, OLED_CS_PIN); // 片選信號拉低,開始傳輸數(shù)據(jù)命令
GPIO_ResetBits(OLED_DC_GPIO, OLED_DC_PIN); // DC信號拉低,表示發(fā)送命令
SPI_SendData8(SPI1, cmd); // 發(fā)送命令
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
GPIO_SetBits(OLED_CS_GPIO, OLED_CS_PIN); // 片選信號拉高,結(jié)束傳輸數(shù)據(jù)命令
}
void OLED_WriteData(uint8_t data) {
GPIO_ResetBits(OLED_CS_GPIO, OLED_CS_PIN); // 片選信號拉低,開始傳輸數(shù)據(jù)
GPIO_SetBits(OLED_DC_GPIO, OLED_DC_PIN); // DC信號拉高,表示發(fā)送數(shù)據(jù)
SPI_SendData8(SPI1, data); // 發(fā)送數(shù)據(jù)
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
GPIO_SetBits(OLED_CS_GPIO, OLED_CS_PIN); // 片選信號拉高,結(jié)束傳輸數(shù)據(jù)
}
void OLED_Init(void) {
GPIO_Init();
SPI_Init();
GPIO_SetBits(OLED_RST_GPIO, OLED_RST_PIN); // OLED復(fù)位
Delay_ms(100);
GPIO_ResetBits(OLED_RST_GPIO, OLED_RST_PIN);
Delay_ms(100);
GPIO_SetBits(OLED_RST_GPIO, OLED_RST_PIN);
OLED_WriteCmd(0xAE); // 關(guān)閉OLED顯示屏
OLED_WriteCmd(0x20); // 設(shè)置內(nèi)存地址模式:水平地址模式
OLED_WriteCmd(0x10); // 設(shè)置高四位地址
OLED_WriteCmd(0xb0); // 設(shè)置起始行為0
OLED_WriteCmd(0xc8); // 設(shè)置掃描方向:從上到下
OLED_WriteCmd(0x00); // 設(shè)置低八位地址
OLED_WriteCmd(0x10); // 設(shè)置高四位地址
OLED_WriteCmd(0x40); // 設(shè)置起始列為0
OLED_WriteCmd(0x81); // 設(shè)置對比度控制
OLED_WriteCmd(0xff); // 對比度值(0-255)
OLED_WriteCmd(0xa1); // 設(shè)置段重映射
OLED_WriteCmd(0xa6); // 設(shè)置正常/反相顯示模式:正常顯示
OLED_WriteCmd(0xa8); // 設(shè)置多路復(fù)用比率
OLED_WriteCmd(0x3F); // 值越大,亮度越大
OLED_WriteCmd(0xa4); // 設(shè)置全局顯示:開啟顯示
OLED_WriteCmd(0xd3); // 設(shè)置顯示偏移
OLED_WriteCmd(0x00); // 不偏移
OLED_WriteCmd(0xd5); // 設(shè)置頻率分區(qū)
OLED_WriteCmd(0xf0); // 頻率分區(qū)
OLED_WriteCmd(0xd9); // 設(shè)置預(yù)充電周期
OLED_WriteCmd(0x22); // 值越大,亮度越弱
OLED_WriteCmd(0xda); // 設(shè)置COM硬件掃描方式
OLED_WriteCmd(0x12); // 按列掃描
OLED_WriteCmd(0xdb); // 設(shè)置VCOMH電壓倍率
OLED_WriteCmd(0x20); // 1.00*VCC
OLED_WriteCmd(0x8d); // 設(shè)置充電泵開關(guān)
OLED_WriteCmd(0x14); // 開啟充電泵
OLED_WriteCmd(0xaf); // 開啟OLED顯示屏
}
void OLED_Puts(uint8_t x, uint8_t y, char *str) {
uint8_t i = 0;
while(str[i] != '?') {
OLED_SetCursor(x + i * 6, y);
for(uint8_t j = 0; j < 6; j++) {
OLED_WriteData(Font_6x8[(str[i] - 32) * 6 + j]);
}
i++;
}
}
int OLED(void) {
OLED_Init();
OLED_Clear();
}
五、總結(jié)
城市環(huán)境監(jiān)測看板,基于STM32F103C8T6主控芯片以及環(huán)境噪聲、溫濕度和粉塵濃度傳感器的數(shù)據(jù)采集,實現(xiàn)了對城市環(huán)境的實時監(jiān)測。該系統(tǒng)能夠準(zhǔn)確獲取環(huán)境噪聲強(qiáng)度、溫濕度和粉塵濃度等信息,為城市規(guī)劃和環(huán)境保護(hù)提供了重要的參考數(shù)據(jù)。
通過使用ADC來采集環(huán)境噪聲傳感器輸出的模擬信號,再結(jié)合處理算法,系統(tǒng)能夠精確測量環(huán)境中的噪聲水平。而DHT11溫濕度傳感器則提供了準(zhǔn)確的溫度和濕度數(shù)據(jù),幫助了解城市環(huán)境的舒適度和變化情況。另外,利用PM2.5粉塵濃度檢測模塊GP2Y10,可以得到城市空氣中細(xì)顆粒物的濃度信息,從而評估空氣質(zhì)量狀況。
這個城市環(huán)境監(jiān)測看板不僅能夠及時獲取環(huán)境數(shù)據(jù),還具備數(shù)據(jù)顯示和報警功能。通過LCD顯示屏,可以直觀地查看當(dāng)前環(huán)境的噪聲、溫濕度和粉塵濃度數(shù)值,以及相應(yīng)的報警狀態(tài)。當(dāng)某項數(shù)據(jù)超過設(shè)定的閾值時,看板將發(fā)出警報提示,提醒人們采取相應(yīng)的措施。
這個項目的設(shè)計和實現(xiàn)為城市規(guī)劃和環(huán)境保護(hù)提供了重要的技術(shù)支持。通過實時監(jiān)測城市環(huán)境的噪聲、溫濕度和粉塵濃度等指標(biāo),可以評估城市生活質(zhì)量、改善城市環(huán)境,從而創(chuàng)建更加宜居、健康的城市環(huán)境。此外,該系統(tǒng)還可以應(yīng)用于智能交通管理、工業(yè)區(qū)域監(jiān)測等領(lǐng)域,提高城市安全性和環(huán)境可持續(xù)發(fā)展水平。
隨著物聯(lián)網(wǎng)和智慧城市的發(fā)展,城市環(huán)境監(jiān)測看板有著廣闊的應(yīng)用前景。通過不斷的技術(shù)創(chuàng)新和數(shù)據(jù)分析,可以更好地了解城市環(huán)境與人類生活的關(guān)系,為構(gòu)建宜居、綠色、可持續(xù)發(fā)展的城市做出積極的貢獻(xiàn)。