1 前言
我是個挺愛琢磨生活細節(jié)的人,在不同的場所我總是會覺得我鼻子不舒服,嗓子也有點干。但是有的地方也挺干凈整潔的呀,我心里時常想:“這也沒人抽煙,也沒刮大風啊?難道是空氣不行?”可空氣好不好,看不見摸不著啊!PM2.5:那些比頭發(fā)絲還細幾十倍的灰塵顆粒,吸進去就賴在肺里不走了,長期下來可不是鬧著玩的。家里有老人小孩的,更得注意這個。二氧化碳:你以為人多的地方只是“悶”?那是CO2超標了!在教室、會議室甚至自己關著門的臥室待久了,頭暈、犯困、注意力不集中,很可能就是它在搗鬼。辦公室下午集體犯困?搞不好就是通風不夠,CO2爆表了!空氣干不干凈?不知道! 全憑感覺,鼻子嗓子不舒服了才后知后覺。開不開窗?很糾結! 外面看著天挺好,開了窗結果可能進來一堆灰(PM2.5);關窗吧,屋里人一多,CO2蹭蹭漲,悶得慌。凈化器/新風開不開?開多久?純靠猜!電費嘩嘩流,效果怎么樣?心里沒底。我就想:能不能做個簡單、便宜又能聯(lián)網的小玩意兒,讓我隨時知道身邊的空氣“底細”?
既然要“隨時知道”,那肯定得聯(lián)網!把數據發(fā)到手機或者網頁上才方便。自己做網絡協(xié)議?想想就頭大!直到我發(fā)現了 W55MH32 這塊板子,簡直是量身定做:“自帶網卡”的MCU: 它最牛的地方就是 芯片里直接集成了硬件TCP/IP協(xié)議棧、以太網MAC和PHY!這意味著什么?我不需要再外接復雜的WiFi模塊或者額外跑協(xié)議棧消耗寶貴的CPU資源。硬件搞定網絡底層,開發(fā)更簡單! 我只需要關心怎么讀取傳感器數據,然后把數據打包發(fā)出去就行。成本低、連線少: 基本上,加個網絡變壓器和RJ45網口,插根網線,它就能直接上網了!穩(wěn)定性也比很多WiFi方案靠譜。性能夠用: 處理幾個傳感器的數據,做點簡單計算,再通過網絡發(fā)出去,W55MH32的性能綽綽有余。
基于以上,我決定動手搞這個項目:本項目以W55MH32 以太網開發(fā)板為核心,通過JW01-CO2傳感器(實時監(jiān)測二氧化碳濃度)與DC01紅外PM2.5傳感器(精準檢測空氣中細顆粒物含量),構建了一套智能化環(huán)境監(jiān)測解決方案。系統(tǒng)通過MQTT協(xié)議與阿里云物聯(lián)網平臺建立穩(wěn)定通信鏈路,實現監(jiān)測數據的云端遠程傳輸、存儲與分析,支持用戶通過移動端或Web端實時查看數據歷史趨勢與告警信息。
創(chuàng)新性地開發(fā)了本地實時數據可視化網頁,設備內置輕量級Web服務器,用戶可直接通過瀏覽器訪問設備IP地址,獲取傳感器數據的動態(tài)刷新顯示(5秒更新一次)。最終達到讓“看不見”的空氣變得“看得見、摸得著、管得住”,用數據指導生活,呼吸得更明白、更健康!
開源項目源碼:https://gitee.com/hupeiyuan0218/w55mh32_aircheck.git
視頻演示:https://www.bilibili.com/video/BV162tzzaEcg/?vd_source=0e464ab43d68457de7bac889723ef284
2 項目環(huán)境
2.1 硬件環(huán)境
W55MH32開發(fā)板
JW01-CO2傳感器
DC01紅外PM2.5傳感器
網線
若干杜邦線
2.2 軟件準備
開發(fā)環(huán)境:Keil uVision 5
Wiznet串口助手(其他串口工具也可以)
阿里云服務器
瀏覽器
2.3 方案圖示

3 注冊阿里云賬號以及建立物模型
注冊賬號在這里不進行贅述,下面我們看如何建立物模型。
3.1 創(chuàng)建產品
搜索物聯(lián)網平臺 → 點擊物聯(lián)網平臺 → 點擊公共實例

選擇設備管理 → 點擊產品 → 點擊創(chuàng)建產品

設置產品名稱、所屬品類、節(jié)點類型、連網方式、數據格式

3.2 添加設備
創(chuàng)建成功產品后點擊添加設備 → 輸入DeviceName

3.3 創(chuàng)建物模型
定義物聯(lián)網模型,選擇產品 → 點擊功能定義 → 點擊前往編輯草稿

點擊添加自定義功能 → 選擇功能類型 → 輸入功能名稱 → 設置標識符 → 設置數據類型 → 設置取值范圍 → 設置步長 → 設置單位 → 設置讀寫類型

3.4 獲取設備連接信息
點擊產品 → 選擇創(chuàng)建的產品 → 點擊Topic類列表 → 選擇物模型通信

訂閱平臺下發(fā)消息的主題:
/sys/a1QsjDLF7Rq/${deviceName}/thing/service/property/set
設備發(fā)布消息主題:
/sys/a1QsjDLF7Rq/${deviceName}/thing/event/property/post
注:兩個主題中的{device_id}需要更改為自己設備的ID
點擊設備管理 → 選擇設備 → 點擊查看

點擊查看DeviceSecret,獲取到設備證書
{
"ProductKey": "a1QsjDLF7Rq",
"DeviceName": "W55MH32",
"DeviceSecret": "5a2b4550d9a3e7a8c19c1c8943d8f710"
}

獲取MQTT連接參數

4 例程修改
本次以MQTT&Aliyun為例:
找到do_mqtt.c文件,把上述中的參數更換為自己阿里云的參數,這些參數我們在上面都有講到,可以自行替換。
mqttconn mqtt_params = { .mqttHostUrl = "a1QsjDLF7Rq.iot-as-mqtt.cn-shanghai.aliyuncs.com", .server_ip = { 0, }, /*Define the Connection Server IP*/ .port = 1883, /*Define the connection service port number*/ .clientid = "a1QsjDLF7Rq.W55MH32|securemode=2,signmethod=hmacsha256," "timestamp=1752545118110|", /*Define the client ID*/ .username = "W55MH32&a1QsjDLF7Rq", /*Define the user name*/ .passwd = "f0dcd6ce4df2493438e42dbd9dd789d348309201ac18f18dd158ddceea45fc5" "d", /*Define user passwords*/ .pubtopic = "/sys/a1QsjDLF7Rq/W55MH32/thing/event/property/post", /*Define the publication message*/ .subtopic = "/sys/a1QsjDLF7Rq/W55MH32/thing/service/property/set", /*Define subscription messages*/ .pubQoS = QOS0, /*Defines the class of service for publishing messages*/ };
先發(fā)送訂閱主題{device_id}的數據包
/sys/a1QsjDLF7Rq/W55MH32/thing/service/property/set訂閱成功后,接收到來自主題的信息SUCCESS。
case SUB: {
ret = MQTTSubscribe(&c, mqtt_params.subtopic,mqtt_params.subQoS,
messageArrived); /* Subscribe to Topics */
printf("Subscribing to %srn", mqtt_params.subtopic);
printf("Subscribed:%srnrn", ret == SUCCESSS ? "success" : "failed");
if (ret != SUCCESSS) {
run_status = ERR;
} else {
run_status = PUB_MESSAGE;
}
break;
}
接下來進行數據發(fā)布,這一步請確保服務ID(Temperature)和屬性名(CurrentTemperature)與您在阿里云上定義的產品模型完全一致(包括大小寫)。發(fā)布成功后更新狀態(tài)為保持連接狀態(tài)。
case PUB_MESSAGE: {
// 動態(tài)構建JSON負載
snprintf(payload_buffer, PAYLOAD_BUFFER_SIZE,
"{"id":"123","version":"1.0","
""params":{"co2":%d,"PM2_5":%d},"
""method":"thing.event.property.post"}",
co2_concentration, concentration);
pubmessage.qos = QOS0;
pubmessage.payload = payload_buffer;
pubmessage.payloadlen = strlen(payload_buffer);
ret = MQTTPublish(&c, (char *)&(mqtt_params.pubtopic),
&pubmessage); /* Publish message */
if (ret != SUCCESSS) {
run_status = ERR;
} else {
printf("publish:%s,%srnrn", mqtt_params.pubtopic, payload_buffer);
run_status = KEEPALIVE;
}
break;
}
完成這兩步就可以成功連接阿里云,并上傳數據到阿里云。
采集數據采用的傳感器采用的是JW01-CO2傳感器、DC01紅外PM2.5傳感器,這兩個傳感器都是通過串口通信,首先進行串口初始化,然后傳感器每秒自動將采集到的數據通過串口發(fā)送。DC01紅外PM2.5傳感器通過串口二連接,JW01-CO2傳感器通過串口三連接。
初始化串口二、串口三
// 串口2初始化函數
void pm25_usart2_init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// 配置TX引腳 (PA0)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置RX引腳 (PA3)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置串口參數
USART_InitStructure.USART_BaudRate = baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
// 使能接收中斷
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能串口
USART_Cmd(USART2, ENABLE);
}
// 串口3初始化
void co2_usart3_init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
// 配置USART3 TX (PB10) 為復用推挽輸出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置USART3 RX (PB11) 為浮空輸入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// USART參數配置
USART_InitStructure.USART_BaudRate = baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART3, &USART_InitStructure);
// 使能接收中斷
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
// 配置USART3中斷
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 優(yōu)先級低于串口2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能串口
USART_Cmd(USART3, ENABLE);
}
初始化完成之后,通過數據格式進行解析,將PM2.5濃度、二氧化碳濃度計算出來
//解析計算PM2.5濃度
void pm25_data_process_usart2(void)
{
// 計算校驗和:前三字節(jié)的低7位之和
uint8_t calculated_checksum = (rx_buffer[0] & 0x7F) +
(rx_buffer[1] & 0x7F) +
(rx_buffer[2] & 0x7F);
calculated_checksum &= 0x7F; // 保留低7位
// 獲取接收到的校驗位(第四字節(jié))
uint8_t received_checksum = rx_buffer[3] & 0x7F;
// 校驗檢查
if (calculated_checksum != received_checksum)
{
checksum_error = 1;
return; // 校驗失敗,丟棄數據
}
checksum_error = 0;
// 計算濃度值
// 濃度值 = (DATAH[6:0] ?????< 7) | DATAL[6:0]
g_pm25_concentration = ((rx_buffer[1] & 0x7F) ?????< 7) | (rx_buffer[2] & 0x7F);
}
//解析計算CO2濃度
void co2_usart3_init(uint32_t baudrate) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
// 配置USART3 TX (PB10) 為復用推挽輸出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置USART3 RX (PB11) 為浮空輸入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// USART參數配置
USART_InitStructure.USART_BaudRate = baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART3, &USART_InitStructure);
// 使能接收中斷
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
// 配置USART3中斷
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 優(yōu)先級低于串口2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能串口
USART_Cmd(USART3, ENABLE);
}
這樣我們就獲取到了環(huán)境空氣質量數據,再創(chuàng)建一個網頁,將PM2.5濃度、CO2濃度進行顯示出來,每五秒刷新一次。
int32_t loopback_tcps(uint8_t sn, uint8_t *buf, uint16_t port) {
int32_t ret;
uint16_t size = 0, sentsize = 0;
switch (getSn_SR(sn)) {
case SOCK_ESTABLISHED:
if (getSn_IR(sn) & Sn_IR_CON) {
setSn_IR(sn, Sn_IR_CON);
}
if ((size = getSn_RX_RSR(sn)) >
0) // Don't need to check SOCKERR_BUSY because it doesn't not occur.
{
if (size > DATA_BUF_SIZE)
size = DATA_BUF_SIZE;
ret = recv(sn, buf, size);
if (ret <= 0)
return ret; // check SOCKERR_BUSY & SOCKERR_XXX. For showing the
// occurrence of SOCKERR_BUSY.
size = (uint16_t)ret;
sentsize = 0;
buf[size] = 0x00;
printf("rece data:%srn", buf);
HttpRequestLine req_line;
if (parse_request_line((char *)buf, &req_line) == 0) {
printf("Method: %sn", req_line.method); // 輸出: GET
printf("URI: %sn", req_line.uri); // 輸出: /
printf("Version: %sn", req_line.version); // 輸出: HTTP/1.1
if (strcmp(req_line.method, "GET") == 0 &&
strcmp(req_line.uri, "/") == 0) {
char html_buffer[2048]; // 確保緩沖區(qū)足夠大
snprintf(html_buffer, sizeof(html_buffer), index_page,
g_co2_concentration, g_pm25_concentration);
uint16_t content_len = strlen(html_buffer);
sentsize = 0;
while (content_len != sentsize) {
ret = send(sn, (uint8_t *)html_buffer + sentsize,
content_len - sentsize);
if (ret < 0) {
close(sn);
return ret;
}
sentsize += ret;
}
disconnect(sn);
close(sn);
} else if (strcmp(req_line.method, "GET") == 0 &&
strcmp(req_line.uri, "/api/sensor") == 0) {
char sensor_data[64];
sprintf(sensor_data, "{"pm25":%u,"co2":%u}", g_co2_concentration,
g_pm25_concentration);
char response[128];
sprintf(response,
"HTTP/1.1 200 OKrn"
"Content-Type: application/jsonrn"
"Connection: closern"
"rn"
"%s",
sensor_data);
send(sn, (uint8_t *)response, strlen(response));
disconnect(sn);
close(sn);
} else {
send(sn, (uint8_t *)HTTP_RESPONSE_404, strlen(HTTP_RESPONSE_404));
disconnect(sn);
close(sn);
}
}
else {
printf("解析失敗!n");
}
}
break;
case SOCK_CLOSE_WAIT:
if ((ret = disconnect(sn)) != SOCK_OK)
return ret;
break;
case SOCK_INIT:
if ((ret = listen(sn)) != SOCK_OK)
return ret;
break;
case SOCK_CLOSED:
if ((ret = socket(sn, Sn_MR_TCP, port, 0x00)) != sn)
return ret;
break;
default:
break;
}
return 1;
}
最后在主函數中添加初始化并在while函數中添加函數
pm25_usart2_init(9600);
co2_usart3_init(9600);
while (1) {
do_mqtt();
pm25_data_process_usart2();
co2_data_process_usart3();
loopback_tcps(1, ethernet_buf, 80);
}
5 功能驗證
程序燒錄完畢,硬件連接完成如下圖所示:

硬件連接完畢,上電通過串口助手打印如下信息:

二氧化碳傳感器需要預熱5-10分鐘進行測試,預熱完成之后即可測試出環(huán)境空氣中CO2和PM2.5濃度含量,同步上傳到阿里云服務器和在本地網頁,每五秒刷新一次。

6 總結
本項目通過整合硬件資源、通信協(xié)議與云平臺能力,成功實現環(huán)境空氣質量檢測功能,驗證了 W55MH32 在物聯(lián)網的實用性,感謝大家的耐心閱讀!如果您在閱讀過程中有任何疑問,或者希望進一步了解這款產品及其應用,歡迎隨時通過私信或評論區(qū)留言。我們會盡快回復您的消息,為您提供更詳細的解答和幫助!
審核編輯 黃宇
-
以太網
+關注
關注
41文章
5936瀏覽量
179763 -
服務器
+關注
關注
13文章
10117瀏覽量
91037 -
開發(fā)板
+關注
關注
26文章
6161瀏覽量
114166 -
阿里云
+關注
關注
3文章
1029瀏覽量
45528 -
MQTT
+關注
關注
5文章
726瀏覽量
24826
發(fā)布評論請先 登錄
汽車空氣質量檢測與改善方案簡介
STM32空氣質量檢測應用教程
【OneNET麒麟座試用申請】空氣質量數據監(jiān)測站
【Thunderboard Sense試用申請】移動空氣質量監(jiān)測站
什么是空氣質量網格化監(jiān)測?
怎樣去設計基于STM32的空氣質量監(jiān)測系統(tǒng)
基于單片機的環(huán)境空氣質量檢測系統(tǒng)的設計
基于單片機的空氣質量監(jiān)測的設計資料分享
網格化空氣質量監(jiān)測站的特點
空氣質量監(jiān)測儀:了解您周圍空氣質量的最佳選擇
空氣質量監(jiān)測儀:掌控空氣質量
空氣質量監(jiān)測系統(tǒng)介紹
空氣質量監(jiān)測系統(tǒng):保護我們的環(huán)境和健康
即刻啟程,踏上W55MH32高性能以太網單片機學習之路!
智能農業(yè)監(jiān)控系統(tǒng):MQTT阿里云平臺監(jiān)測+內置Web網頁控制+代碼解析

從零搭建W55MH32以太網環(huán)境空氣質量檢測系統(tǒng):MQTT阿里云平臺監(jiān)測+內置Web網頁顯示+代碼全解析
評論