嵌入式实战项目:基于STM32的车载消毒净化器设计与实现
招标
发布时间:
2026-08-10
发布于
--
收藏
公告内容
项目编号
立即查看
项目预算
立即查看
采购单位
立即查看
供应商
立即查看
采购代理
立即查看
公告详情
您当前为:【游客状态】,公告详情仅对登录用户开放,
登录/注册
后查看完整商机。全国免费咨询热线:400-888-7022

嵌入式实战项目:基于STM32的车载消毒净化器设计与实现

原创 梁工*开通会员可解锁* 嵌入式单片机

一、前言

1.1 项目开发背景

随着汽车成为日常出行的核心工具,车内密闭空间易滋生细菌、异味,且驾乘人员频繁进出带来的空气污染问题日益突出。传统车载香薰、消毒喷雾等方式存在操作繁琐、无法自动启停、缺乏实时监测等问题,难以满足智能化、健康化的车内环境管理需求。

本项目基于 STM32F103ZET6 主控芯片,整合 KQM6600 气体检测、热释电人体感应、DHT11 温湿度监测、继电器控制、TFTLCD 可视化显示及 ESP8266 WiFi 通信等功能,开发一套智能车载消毒净化器。STM32F103ZET6 凭借丰富的外设接口和稳定的处理能力,可实现车内气体浓度、温湿度的实时监测,结合人体感应实现消毒功能的智能启停,并通过巴法云平台实现数据远程监控,解决传统车载消毒方式的痛点。

1.2 设计实现的功能

(1)手动控制消毒:用户通过物理按键触发继电器开关,手动启停消毒模块(继电器模拟 消毒器工作);

(2)自动消毒触发:KQM6600 检测到有害气体浓度超过预设阈值时,自动进入消毒准备流 程;

(3)人体感应保护:自动模式下,仅在热释电传感器检测到车内无人时,才触发继电器开启消毒,避免消毒物质对人体造成影响;

(4)环境参数监测:DHT11 实时采集车内温湿度数据,KQM6600 实时监测有害气体浓度,数据精准且更新及时;

(5)可视化显示:240*320 像素 TFTLCD 屏实时显示气体浓度、温湿度、消毒器工作状态(开启 / 关闭)、工作模式(手动 / 自动);

(6)远程数据上传:ESP8266 WiFi 模块将温湿度、气体浓度、继电器状态等数据上传至巴法云物联网平台,支持远程查看;

(7)模式切换:支持手动 / 自动模式一键切换,切换状态同步显示在 LCD 屏并上传至云端。

1.3 项目硬件模块组成

(1)主控芯片:STM32F103ZET6基于 ARM Cortex-M3 架构,主频最高 72MHz,内置 256KB Flash 和 64KB SRAM,具备多个 GPIO 口、USART 串口、ADC、定时器等外设,负责数据采集、逻辑判断、模块驱动及通信控制,是系统核心。

(2)气体检测模块:KQM6600一款高精度有害气体检测传感器,支持 VOC、甲醛等多种有害气体浓度检测,可直接接入 STM32 串口 引脚,检测范围广、响应速度快,适配车载环境的气体监测需求。

(3)人体感应模块:热释电红外传感器(SR602)基于热释电效应,可检测人体发出的红外线,有效探测距离 2-7 米,输出高低电平信号,用于判断车内是否有人,为自动消毒提供安全触发条件。

(4)执行模块:继电器模块采用 5V 低电平触发的单路继电器,与 STM32 GPIO 口连接,用于控制消毒器的电源通断,触点负载能力强,可适配不同功率的车载消毒模块。

(5)温湿度检测模块:DHT11一款低成本、易集成的数字温湿度传感器,单总线通信方式,测温范围 0-50℃,测湿范围 20%-90% RH,满足车载环境的温湿度监测需求。

(6)显示模块:TFTLCD 屏(240320 像素)SPI 接口通信,240*320 高分辨率彩色显示,支持实时刷新气体浓度、温湿度、设备状态等信息,直观呈现系统工作情况。

(7)无线通信模块:ESP8266 WiFi 模块串口转 WiFi 通信,支持 AT 指令控制,可连接巴法云物联网平台,实现本地数据的远程上传,功耗低、适配车载 12V 转 5V 电源。

(8)交互按键:独立按键 ×2分别实现 “消毒启停” 和 “手动 / 自动模式切换” 功能,配合软件消抖处理,避免误操作。

(9)电源模块:车载 12V 转 5V/2A 电源适配汽车 12V 电瓶供电,转换为 5V 稳定电压,为主控、传感器、继电器等模块供电,满足车载场景的电源需求。

1.4 设计思路

本项目以 STM32F103ZET6 为主控,采用模块化设计,各模块协同实现智能消毒功能,具体逻辑如下:

(1)系统初始化:上电后完成 STM32 外设(GPIO、ADC、USART、SPI 等)、KQM6600、热释电、DHT11、继电器、TFTLCD、ESP8266 的初始化配置;

(2)数据采集:ADC 采集 KQM6600 的气体浓度模拟信号,单总线读取 DHT11 温湿度数据,GPIO 检测热释电传感器的人体感应信号;

(3)模式判断:根据按键设置的工作模式(手动 / 自动)执行不同逻辑:

手动模式:直接响应 “消毒启停” 按键,控制继电器开关,同步更新 LCD 显示和云端数据;

自动模式:当 KQM6600 检测的气体浓度超过阈值,且热释电检测到无人时,自动触发 继电器开启消毒,达到预设消毒时长后自动关闭;

(4)可视化显示:TFTLCD 屏循环刷新气体浓度、温湿度、工作模式、消毒状态等信息;

(5)数据上传:ESP8266 通过 USART 接收主控发送的传感器数据和设备状态,按巴法云协议封装后上传至云端;

(6)安全保护:自动模式下,若热释电检测到有人,立即关闭继电器停止消毒,保障人体安全。

1.5 开发环境介绍

软件开发环境采用 Keil MDK-ARM V5,作为 ARM 微控制器开发的行业标准工具,在STM32 开发中应用广泛,具备以下优势:

(1)内置 STM32F10x Standard Peripheral Library 标准外设库,简化外设配置,降低开发难度;

(2)支持 ST-Link V2 等调试工具在线调试,可实时查看变量、寄存器状态和程序流程,便于问题定位;

(3)兼容 C 语言、汇编语言,可与第三方工具集成,扩展性强;

(4)网络上有丰富的教程、案例和解决方案,适合新手上手。

硬件调试工具采用 ST-Link V2 仿真器,支持程序下载和在线调试,通过 SWD 接口与STM32连接,传输稳定、操作便捷;串口调试助手用于调试 ESP8266 的 AT 指令和数据上传功能。

1.6 模块技术详情介绍

(1)KQM6600 气体检测模块

基于电化学原理,输出 0-5V 模拟电压信号,电压值与有害气体浓度成正比(如 0V 对应 0ppm,5V 对应预设最大浓度)。STM32 通过 ADC 将模拟电压转换为数字量,经校准后得到实际气体浓度值,检测响应时间≤3 秒。

(2)热释电红外传感器(SR602)

由红外探测元件、菲涅尔透镜、信号处理电路组成,当检测到人体红外辐射时,输出高电平;无人时输出低电平。传感器具备抗干扰设计,可过滤阳光、车灯等非人体红外信号,适配车载复杂环境。

(3)继电器模块

采用电磁式继电器,低电平触发,线圈电压 5V,触点最大负载 250V/10A(交流)、30V/10A(直流),可直接控制车载消毒器的电源回路。STM32 通过 GPIO 输出低电平触发继电器吸合,高电平释放,实现消毒器的通断控制。

(4)DHT11 温湿度模块

单总线通信,无需额外外设,仅需一根 GPIO 口即可完成数据读取。每次通信输出 40 位数据,包含湿度整数、小数(固定 0)、温度整数、小数(固定 0)、校验位,数据更新周期 1 秒,满足车载环境的实时监测需求。

(5)ESP8266 WiFi 模块

支持 802.11b/g/n 协议,串口波特率默认 9600bps,通过 AT 指令可配置 WiFi 连接、数据上传等功能。本项目中,STM32 通过 USART 向 ESP8266 发送封装好的传感器数据,模块将数据以 TCP/UDP 方式上传至巴法云平台,实现远程监控。

(6)TFTLCD 屏(240*320 像素)

SPI 接口通信,传输速率快,支持 16 位真彩显示,可显示中文、数字、图形等内容。通过字库驱动可实现气体浓度、温湿度等数据的可视化展示,刷新率≥10 帧 / 秒,无卡顿。

二、硬件选型以及硬件连接图

(1)继电器模块

(2)热释电模块

(3)空气质量检测模块

(4)温湿度模块

(5)ESP8266联网模块

三、关键代码

3.1 相关模块驱动代码

(1)继电器模块

#include "relay.h" void Relay_Config(void){ //1、开时钟 -- 供能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //2、初始化 GPIO_InitTypeDef relay; relay.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出 relay.GPIO_Pin = GPIO_Pin_11; relay.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&relay); }

(2)热释电模块

#include "pir.h" //热释电模块//低电平 -- 没有人//高电平 -- 有人void Pir_Config(void){ //1、开时钟 -- 供能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE); //2、初始化 GPIO_InitTypeDef pir; pir.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 pir.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOF,&pir); }

(3)空气质量检测模块

#include "kqm660.h"#include "bsp_lcd.h" float CH20MAX = 0.03;/*******************接线: G-------GND V-------3.3V A-------PA3 B-------PA2*******************/void kqm660_config(void){ USART_InitTypeDef USART_InitStruct = {0}; GPIO_InitTypeDef GPIO_InitStruct = {0}; NVIC_InitTypeDef NVIC_InitStruct = {0}; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOA,&GPIO_InitStruct); //浮空输入 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; //引脚 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3; //2.配置工作模式 GPIO_Init(GPIOA,&GPIO_InitStruct); //开启usart2时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); USART_InitStruct.USART_BaudRate = 9600;//波特率 USART_InitStruct.USART_Parity = USART_Parity_No;//奇偶失能 USART_InitStruct.USART_WordLength = USART_WordLength_8b;//8位数据 USART_InitStruct.USART_StopBits = USART_StopBits_1;//1停止位 USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//发送使能,接收使能 USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制失能 USART_Init(USART2,&USART_InitStruct); //中断配置 //数据接收中断--收到数据就会进中断 USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); //空闲中断--》数据接收完成,进入空闲中断 USART_ITConfig(USART2,USART_IT_IDLE,ENABLE); //NVIC配置 NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); //使能USART外设 USART_Cmd(USART2,ENABLE);} void Usart2_Send_Byte(char ch){ //确保上一次数据发生完成 while(USART_GetFlagStatus(USART2,USART_FLAG_TC)!=SET); //发送数据 USART2->DR = ch;}//发送数组void Usart2_Send_NByte(uint8_t data[],uint32_t len){ uint32_t i; for(i=0;i <len;i++)< span> </len;i++)<> Usart2_Send_Byte(data[i]);} Kqm6600_data_Typedef kqm6600={0}; void USART2_IRQHandler(void){ volatile uint32_t data; //接收中断 if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET) { //接收数据 kqm6600.data_buf[kqm6600.data_count++] = USART2->DR;//数据来源 kqm6600.data_count %= 8; USART_ClearITPendingBit(USART2,USART_IT_RXNE); } //空闲中断 if(USART_GetITStatus(USART2,USART_IT_IDLE)!=RESET) { //空闲中断 kqm6600.data_rx_flag = 1; //清除空闲中断标志 data = USART2->SR; data = USART2->DR; }} void kqm660_data(void){ uint32_t i; uint8_t check_sum = 0; if(kqm6600.data_rx_flag == 1) { for(i=0;i <kqm6600.data_count;i++)< span> </kqm6600.data_count;i++)<> { printf("%#x ",kqm6600.data_buf[i]); } printf("\r\n"); if(kqm6600.data_buf[0] == 0X5F) { printf("地址码正确\r\n"); if(kqm6600.data_buf[1] == 0XFF && kqm6600.data_buf[3] == 0XFF) { printf("正在预热\r\n"); } else { //printf("有效数据\r\n"); for(i=0;i<7;i++) { check_sum+=kqm6600.data_buf[i]; } check_sum &= 0xff; if(check_sum == kqm6600.data_buf[7]) { char kqm_buf[100]={0}; //printf("检验成功\r\n"); kqm6600.voc = ((kqm6600.data_buf[1]<<8)|kqm6600.data_buf[2])*0.1; kqm6600.ch2o = ((kqm6600.data_buf[3]<<8)|kqm6600.data_buf[4])*0.01; kqm6600.co2 = ((kqm6600.data_buf[5]<<8)|kqm6600.data_buf[6])*1; printf("VOC:%.2fPPM\r\n",kqm6600.voc); printf("CH2O:%.2fmg/m3\r\n",kqm6600.ch2o); printf("CO2:%.2fPPM\r\n",kqm6600.co2); sprintf(kqm_buf,"CO2指数:%.2fPPM",kqm6600.co2); LCD_ShowStringAndHz(0,260,200,16,16,(u8*)kqm_buf,0); memset(kqm_buf,0,sizeof(kqm_buf)); sprintf(kqm_buf,"VOC指数:%.2fPPM",kqm6600.voc); LCD_ShowStringAndHz(0,280,200,16,16,(u8*)kqm_buf,0); memset(kqm_buf,0,sizeof(kqm_buf)); sprintf(kqm_buf,"CH2O指数:%.2fmg/m3",kqm6600.ch2o); LCD_ShowStringAndHz(0,300,200,16,16,(u8*)kqm_buf,0); memset(kqm_buf,0,sizeof(kqm_buf)); } else { printf("检验失败\r\n"); } } } else { printf("地址码错误"); } kqm6600.data_rx_flag = 0; kqm6600.data_count = 0; memset(kqm6600.data_buf,0,sizeof(kqm6600.data_buf)); }}

(4)DHT11模块

#include "dht.h"#include "delay.h"#include "bsp_lcd.h"#include "stdio.h"#include "string.h"#include "esp8266.h"//初始化 PG11 DHT11数据引脚void Dht_Config(u8 mode){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE); GPIO_InitTypeDef dh = {0}; dh.GPIO_Pin = GPIO_Pin_11; dh.GPIO_Speed = GPIO_Speed_50MHz; if(mode == DHT_IN) { dh.GPIO_Mode = GPIO_Mode_IN_FLOATING; } else { dh.GPIO_Mode = GPIO_Mode_Out_OD; } GPIO_Init(GPIOG,&dh);} //获取DHT11数据函数 float Tem = 0;//温度float Hum = 0; //湿度u8 data[5] = {0};//存放40位数据 uint8_t Get_DhtData(void){ u8 check = 0;//40位数据校验 u8 i = 0;//40位数据循环接收 u8 count = 0;//时间校验 //起始信号 Dht_Config(DHT_OUT);//输出模式 DHT_L; Delay_ms(20); DHT_H; //目的是终止发送低电平 //响应信号 Dht_Config(DHT_IN);//输入模式 while(DHT_DATA == 1) //等待低电平到来 { Delay_us(1); count++; if(count>100) { return 1; } } count = 0; while(DHT_DATA == 0) //等待高电平到来 { Delay_us(1); count++; if(count>100) { return 2; } } count = 0; //接收40位数据 for(i=0;i<40;i++) { //等待低电平 while(DHT_DATA == 1) //等待低电平到来 { Delay_us(1); count++; if(count>100) { return 3; } } count = 0; while(DHT_DATA == 0) //等待高电平到来 { Delay_us(1); count++; if(count>100) { return 4; } } count = 0; //判断该位是0还是1 Delay_us(30); if(DHT_DATA == 1)//存1 { data[i/8] |= (0x1<<(7-i%8)); } else //存0 { data[i/8] &= ~(0x1<<(7-i%8)); } } //数据校验 if(data[4] == data[0]+data[1]+data[2]+data[3]) { char dht_buf[100] = {0}; Hum = data[0]+data[1]/10.0; Tem = data[2]+data[3]/10.0; printf("湿度:%2.1f 温度:%2.1f\r\n",Hum,Tem);//%d %c %s char buf[30] = {0}; sprintf(buf,"今日温度:%2.1f",Tem); LCD_ShowStringAndHz(0,220,200,16,16,(u8*)buf,0); memset(buf,0,sizeof(buf)); sprintf(buf,"今日湿度:%2.1f",Hum); LCD_ShowStringAndHz(0,240,200,16,16,(u8*)buf,0); memset(buf,0,sizeof(buf)); // sprintf(dht_buf,"cmd=2&uid=%s&topic=Tem&msg=#%2.1f#\r\n",PRODUCTID,Tem);// USART3_SendArray((u8*)dht_buf,strlen(dht_buf));// sprintf(dht_buf,"cmd=2&uid=%s&topic=Hum&msg=#%2.1f#\r\n",PRODUCTID,Hum);// USART3_SendArray((u8*)dht_buf,strlen(dht_buf)); } else //校验失败 { printf("温湿度数据校验失败\r\n"); return 0; }}

(5)WIFI模块

#include "esp8266.h"#include "stdio.h"#include "delay.h"#include "string.h"#include "stdlib.h" USART3_DataStruct USART3_Data={0}; //PB10:TXD PB11:RXDvoid usart3_config(void){ //开时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //配IO口模式 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;//复用推挽 GPIO_Init(GPIOB,&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOB,&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出 GPIO_Init(GPIOE,&GPIO_InitStruct); //配usart的工作模式 USART_InitTypeDef USART3_InitStruct; USART3_InitStruct.USART_BaudRate=115200;//波特率 USART3_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件控制失能 USART3_InitStruct.USART_WordLength=USART_WordLength_8b;//数据长度 USART3_InitStruct.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;//接受 发送 USART3_InitStruct.USART_StopBits=USART_StopBits_1;//1位停止位 USART3_InitStruct.USART_Parity=USART_Parity_No;//关奇偶校验 USART_Init(USART3,&USART3_InitStruct); USART_Cmd(USART3,ENABLE);//使能串口3 //接收中断配置 USART_ITConfig(USART3,USART_IT_RXNE,ENABLE); USART_ITConfig(USART3,USART_IT_IDLE,ENABLE); //NVIC配置 NVIC_EnableIRQ(USART3_IRQn); NVIC_SetPriority(USART3_IRQn,1); WIFI_DISABLE; Delay_ms(50); WIFI_ENABLE;} void WIFI_Init(void){ usart3_config(); Delay_ms(2000); Connect_AP(MY_SSID,MY_PASSWORD); //连接热点 Connect_Server(MY_IP,MY_PORT); //连接巴法云} void USART3_SendData(u8 data){ while(USART_GetFlagStatus(USART3,USART_FLAG_TC) == RESET) {}//等待上一帧数据发送完成 USART_SendData(USART3,data);//发送数据} void USART3_SendStr(char * p){ while(*p != '\0') { USART3_SendData(*p); p++; }} void USART3_SendArray(u8 Arr[] ,u16 DataLenth){ u16 i=0; while(DataLenth --) { USART3_SendData(Arr[i++]); }} void USART3_IRQHandler(void){ if(USART_GetITStatus(USART3,USART_IT_RXNE) == SET) { USART_ClearITPendingBit(USART3,USART_IT_RXNE); u8 data = USART3->DR; USART1->DR = data; USART3_Data.RX_buff[USART3_Data.RX_count]=USART3->DR; USART3_Data.RX_count++; if(USART3_Data.RX_count > 1032) USART3_Data.RX_count=0; } if(USART_GetITStatus(USART3,USART_IT_IDLE) == SET) { u8 flag=0; flag=USART3->SR; flag=USART3->DR;//清中断 USART3_Data.USART_RevOverflag=1;//接收完成 }} /************************************字符串查找参数1:在哪个字符串中查找参数2:要查找的内容参数3:超时时间返回值:查找结果*************************************/char * FindStr(char* dest, char* src, uint32_t timeout){ while(timeout-- && (strstr(dest, src) == NULL)) { Delay_ms(1); } return strstr(dest, src);} /** * 功能:ESP8266发送指令获取应答 * 参数: * cmd -- 指令字符串 * ack -- 应答字符串 * timeout -- 应答溢出时长 * check_cnt -- 循环发送指令的次数(可能模组没反应过来,多发几次) * 返回值:0 -- 成功 >0 -- 失败 */ uint8_t ESP8266_SendCmd_RecAck(char *cmd, char *ack, uint32_t timeout, uint8_t check_cnt){ uint32_t len = strlen((char *)cmd); USART3_Data.RX_count = 0; memset(USART3_Data.RX_buff, 0, sizeof(USART3_Data.RX_buff)); do{ //发送命令 if(cmd != NULL) { USART3_SendArray((uint8_t *)cmd, len); USART3_SendStr("\r\n"); } //查询应答字符串 if(ack != NULL) { if(FindStr((char *)USART3_Data.RX_buff, ack, timeout) != NULL) //等待串口接收应答,并解析 { USART3_Data.RX_count = 0; return 0;//接收到正确应答 } } }while(--check_cnt); return 1;//失败} //退出透传模式void Exit_Scotch(void){ USART3_SendStr("+++"); Delay_ms(500);} /************************************STA模式 连接2.4GHz的热点参数1:SSID,热点名参数2:PASSWORD,热点密码返回值:*************************************/u8 Connect_AP(char *SSID,char *PASSWORD){ char sendarr[256]={0}; Exit_Scotch();//退出传透模式 if(ESP8266_SendCmd_RecAck("AT","OK",500,2)) { printf("AT 测试失败\r\n"); return 1; } else printf("AT 测试成功\r\n"); if(ESP8266_SendCmd_RecAck("AT+CWMODE=1","OK",500,2)) { printf("AT+CWMODE 设置失败\r\n"); return 2; } else printf("AT+CWMODE 设置成功\r\n"); sprintf(sendarr,"AT+CWJAP=\"%s\",\"%s\"",SSID,PASSWORD); if(ESP8266_SendCmd_RecAck(sendarr,"OK",10000,2)) { printf("AT+CWJAP 设置失败\r\n"); return 3; } else { printf("AT+CWJAP 设置成功\r\n"); printf("连接热点成功\r\n"); } return 0;} /************************************连接服务器参数1:IP,服务器域名或IP地址参数2:PORT,服务器端口号返回值:*************************************/u8 Connect_Server(char *IP,u16 PORT){ char sendarr[256]={0}; if(ESP8266_SendCmd_RecAck("AT+CWJAP?","OK",5000,2) == 1) { printf("未检测到连接热点\r\n重连中...\r\n"); if(Connect_AP(MY_SSID,MY_PASSWORD)) { printf("未检测到热点,退出\r\n"); return 1; } } sprintf(sendarr,"AT+CIPSTART=\"TCP\",\"%s\",%d",IP,PORT); if(ESP8266_SendCmd_RecAck(sendarr,"OK",2000,2) == 1) { printf("连接服务器失败,退出\r\n"); return 2; } if(ESP8266_SendCmd_RecAck("AT+CIPMODE=1","OK",500,2) == 1) { printf("开启透传模式失败,退出\r\n"); return 3; } if(ESP8266_SendCmd_RecAck("AT+CIPSEND",">",500,2) == 1) { printf("进入透传模式失败,退出\r\n"); return 4; } return 0;} //巴法云下发指令解析函数#include "beep.h"void Bafayun_Data_Anlyze(void){ char * message = NULL; if(USART3_Data.USART_RevOverflag) { //cmd=2&uid=xxxx&topic=tem&msg=1 //利用strstr关键词找到msg字符串 message = strstr((char *)USART3_Data.RX_buff, "msg"); //找到msg=1,进行右移4个字符,就是字符'1' u8 LEDSwitch=*(message+4); switch(LEDSwitch) { case '1':BEEP_ON;break; case '0':BEEP_OFF;break; } //清除缓冲区数据,1.要清除的缓冲区,2.填充的数据,3.数据长度 memset(USART3_Data.RX_buff,0,200); USART3_Data.RX_count = 0; USART3_Data.USART_RevOverflag = 0; //还原为未接收完成的标志 }} //订阅:输入需要订阅的主题即可void Subscribe_Bemfa(char *topic){ char sendarr[256]={0}; sprintf(sendarr,"cmd=1&uid=%s&topic=%s\r\n",PRODUCTID,topic); USART3_SendArray((u8*)sendarr,strlen(sendarr));} //上传指令1void Bafa_publish(char *topic,uint32_t msg1){ char str[80] = {0}; memset(str,0,sizeof(str));//数组清零 sprintf(str,"cmd=2&uid=%s&topic=%s&msg=#%d\r\n",PRODUCTID,topic,msg1); USART3_SendArray((u8*)str,strlen(str)); } //上传指令2void Bafa_publish2(char *topic,uint32_t msg1,uint32_t msg2){ char str[80] = {0}; memset(str,0,sizeof(str));//数组清零 sprintf(str,"cmd=2&uid=%s&topic=%s&msg=#%d#%d\r\n",PRODUCTID,topic,msg1,msg2); USART3_SendArray((u8*)str,strlen(str));} //上传指令3void Bafa_publish3(char *topic,float msg1,float msg2,float msg3){ char str[80] = {0}; memset(str,0,sizeof(str));//数组清零 sprintf(str,"cmd=2&uid=%s&topic=%s&msg=#%.2f#%.2f#%.2f\r\n",PRODUCTID,topic,msg1,msg2,msg3); USART3_SendArray((u8*)str,strlen(str));}

3.2 主函数框架代码

#include "stm32f10x.h"#include "stdio.h"#include "string.h"#include "delay.h"#include "led.h"#include "beep.h"#include "key.h"#include "bsp_lcd.h"#include "uart.h"#include "dht.h"#include "esp8266.h"#include "relay.h"#include "pir.h"#include "kqm660.h" extern const unsigned char gImage_xd[153608];int main(void){ int keynum; SysTick_Config(72000);//滴答定时器初始化 -- 1ms Led_Config(); Beep_Config(); Key_Config(); Usart1_Config(); Relay_Config();//继电器初始化 Pir_Config();//热释电初始化 TFTLCD_Init(); LCD_ShowPicture(0,0,240,320,(u8 *)gImage_xd); LCD_ShowStringAndHz(72,0,240,16,16,(u8 *)"车载消毒系统",0); kqm660_config(); WIFI_Init(); Subscribe_Bemfa("TemHum"); Subscribe_Bemfa("VOC"); while(1) { if(ledtime>500) { LED_LS(); ledtime=0; } if(keytime>10)//10ms的按键检测任务 { keynum=Key_Getnum(); switch(keynum) { case 1: RELAY_ON;//手动打开消毒模式 Beep_short(); break; case 2: RELAY_OFF;//手动关闭消毒模式 Beep_short(); break; case 3: Beep_short();break; case 4: Beep_short();break; } keytime=0;//清零重新计数 } if(kqmtime>500) { kqm660_data(); kqmtime=0; } if(dhttime>300) { Get_DhtData(); dhttime=0; } if(xdmtime>1000) { if((kqm6600.voc>0.5)&&(PIR==0))//有害气体超标且没有人在车上的时候 { RELAY_ON; } else { RELAY_OFF; } xdmtime=0; } if(baftime>1500) { Bafa_publish2("TemHum",Tem,Hum); Bafa_publish3("VOC",kqm6600.co2,kqm6600.voc,kqm6600.ch2o); baftime=0; } } }

3.3 核心代码说明

(1)系统初始化部分

System_Init()完成所有模块的初始化配置:KQM6600模块初始化, GPIO 初始化配置传感器、继电器、按键的输入输出模式,SPI 初始化驱动TFTLCD 屏,USART 初始化实现与ESP8266 的串口通信。初始化完成后,LCD 屏显示初始化提示,ESP8266 连接巴法云平台,为系统运行做好准备。

(2)数据采集部分

KQM6600:通过 ADC 采集 0-5V 模拟信号(分压后 0-3.3V),转换为 0-4095 的数字量,再校准为 0-1000ppm 的气体浓度值;

DHT11:通过单总线通信读取温湿度数据,校验后提取湿度和温度的整数部分;

热释电传感器:GPIO 检测高低电平,多次采样消抖后判断车内是否有人;

继电器:GPIO 读取输出电平,获取当前消毒器的工作状态。

(3)核心逻辑部分

手动模式:响应 “消毒启停” 按键,直接控制继电器开关,同步更新 LCD 显示和云端数据;

自动模式:当 KQM6600 检测的气体浓度超过 500ppm 阈值,且热释电检测到无人时,自动开启继电器进行 30 秒消毒,消毒完成后自动关闭;若检测到有人,立即禁止消毒,保障人体安全;

模式切换:按键触发手动 / 自动模式切换,切换状态实时显示在 LCD 屏。

(4)显示与通信部分

TFTLCD 屏:循环刷新工作模式、气体浓度、温湿度、消毒状态,不同状态采用不同颜色标注(如消毒中为红色),直观呈现系统状态;

ESP8266:每 5 秒将气体浓度、温湿度、继电器状态封装为指定格式,通过 TCP 上传至巴法云平台,支持远程查看车内环境和设备状态。

四、总结

本项目基于 STM32F103ZET6 主控芯片,开发了一套适配车载场景的智能消毒净化器,整合气体检测、人体感应、温湿度监测、继电器控制、可视化显示和远程数据上传功能,实现了手动 / 自动双模式消毒,解决了传统车载消毒方式操作繁琐、无安全保护、无法远程监控的痛点。

系统优势:

(1)安全可靠:自动模式下仅在车内无人时触发消毒,避免消毒物质对人体造成伤害;继电器具备过流保护,适配车载电源环境;

(2)智能化程度高:基于气体浓度阈值自动触发消毒,无需人工干预;温湿度、气体浓度实时监测,数据精准;

(3)可视化与远程化:240*320 TFTLCD 屏直观显示设备状态,ESP8266 上传数据至巴法云, 支持远程查看车内环境;

(4)适配性强:车载 12V 转 5V 电源供电,体积小、功耗低,可直接安装在汽车中控台或后备箱,适配不同车型。

未来拓展方向:

(1)多阈值设置:支持按键设置不同的气体浓度阈值,适配不同消毒需求;

(2)远程控制:巴法云平台添加远程控制指令,支持手机 APP 远程启停消毒;

(3)异常报警:当气体浓度超标或消毒器故障时,通过 ESP8266 推送报警信息至手机;

(4)低功耗优化:增加睡眠模式,车辆熄火后系统进入低功耗状态,仅保留核心传感器监 测,降低电瓶耗电;

(5)多传感器融合:增加 PM2.5 传感器,全面监测车内空气质量,提升消毒的精准性。

微信扫一扫关注该公众号

潜在客户预测
点击查看详情>
合作机会