STM32毕设项目分享:基于STM32的智能仓库环境监控系统(物联网、FATFS文件操作系统) 一、成品简介1.实物成品成品展示2.物联网APP物联网APP界面物联网显示界面3.SD卡存储SD卡文本文档打开界面二、项目简介1.功能详解基于单片机的智能仓库环境监控系统语音环境参数采集使用DHT11温湿度传感器采集仓库内温度和湿度TVOC-301气体检测模块采集甲醛和CO₂浓度SR-04超声波测距模块用于模拟货架间距或货物堆放距离测量。本地实时显示0.96寸OLED屏幕实时显示当前温湿度、气体浓度及距离数值方便现场巡查。数据本地存储通过SDIO接口连接Micro SD卡并移植FATFS文件系统将环境数据以文本日志形式追加写入date.txt文件实现断电不丢失的历史记录存储。远程数据上传ESP8266 Wi-Fi模块连接无线网络通过MQTT协议将采集数据上传至OneNet物联网云平台支持JSON格式数据包定时发送。远程监控与可视化手机App基于uni-app框架通过调用OneNet API接口实时拉取并显示温度、湿度、甲醛、CO₂等参数实现远程随时查看仓库环境状态。异常预警与报警在OneNet平台配置规则引擎当温湿度或气体浓度超出预设阈值时自动触发邮件报警同时本地蜂鸣器发声提醒实现双重告警。数据可靠性处理对传感器数据进行校验和检查如TVOC-301帧校验及低通滤波超声波测距确保采集数据的准确性和稳定性。硬件框图流程图2.主要器件STM32F103ZeT6单片机DHT11温湿度传感器TVOC-301平面气体传感器模块可检测甲醛和CO₂SR-04超声波测距模块ESP8266-01S Wi-Fi模块0.96寸OLED显示屏I²C接口Micro SD卡用于本地数据存储有源蜂鸣器用于本地声音报警3.原理图设计该系统供电没有额外设计将充电宝或者充电线插入单片机输入端即可供电。使用需注意SD卡上电会先检测SD卡当SD卡未插入不记录数据。4.PCB设计三、主要模块1.ESP8266物联网模块ESP8266-01S是一款高度集成的串口转Wi-Fi模块内置TCP/IP协议栈通过串口和STM32F103ZET6连接波特率为115200通过发送标准AT指令集对模块进行控制使用中需要注意模块的串口发送缓冲区有限单次发送的JSON数据包不可过大避免发送导致栈或者数组溢出所有AT指令以“\r\n”结尾2.SR-04超声波模块SR-04是一款利用声波反射原理进行非接触式测距的常用模块测量范围2cm~300cm使用时主控需在Trig引脚产生一个持续10微秒以上的高电平脉冲模块便会自动发射40kHz的超声波并开始计时当接收到回波后Echo引脚会输出一个高电平其持续时间即为声波往返的时间。根据公式“距离 高电平时间 × 声速340m/s/ 2”计算出实际距离单次测量值会出现跳变因此代码中采用了一阶低通滤波算法对原始距离进行平滑处理。3.DHT11模块DHT11是一款数字式温湿度复合传感器内部集成电容式湿敏元件和热敏电阻采用单总线One-Wire协议与主控通信能够同时输出温度和湿度数据。该模块对时序精度要求较高建议使用硬件定时器或精确的微秒级延时函数。4.TVOC-301平面气体传感器模块TVOC-301是一款微型空气质量检测模组可同时检测甲醛量程0~2000μg/m³和二氧化碳量程0~5000ppm浓度该模块采用UART串口通信波特率固定为9600。模块每秒钟输出一帧固定长度为9字节的数据包帧头固定为0x2C和0xE4随后依次为TVOC浓度高字节、低字节甲醛浓度高字节、低字节CO₂浓度高字节、低字节以及最后1字节的校验和计算方法为前面8个字节的累加和取低8位。STM32通过串口中断逐字节接收状态机判断当前字节所属字段。5.Micro SD卡本地存储模块Micro SD卡作为本地数据存储介质用于实现环境监测日志的持久化保存即使在网络中断的情况下也能完整记录历史数据。本系统通过STM32F103ZET6自带的SDIO接口与Micro SD卡进行通信每次写入操作后必须及时关闭文件因为FATFS在写入时会将数据暂存在缓存中若意外断电不关闭文件不仅当前数据会丢失还可能导致文件分配表损坏。6.单片机主控STM32F103ZET6 是 意法半导体公司所推出的一款基于 ARM Cortex-M3 内核的高性能微控制器主频具有 72MHz具备 512KB Flash 和 64KB RAM支持丰富的通信外设接口包括多个 UART、I²C、SPI、CAN 以及 SDIO 接口非常适合本系统中多传感器数据采集、SD 卡存储与 Wi-Fi 通信的需求。四、写作内容参考1.摘要参考内容随着现代物流和仓储行业的快速发展传统依赖人工巡检的仓库环境监控方式难以满足对温湿度、空气质量等关键参数实时性、准确性和智能化管理的需求。为此本设计提出一种基于STM32的智能仓库环境监控系统在实现对仓储环境的全面管理、本地存储与远程预警。本设计包含两大部分第一部分系统硬件选型设计以STM32F103ZET6单片机为主控使用DHT11温湿度传感器、TVOC-301气体检测模块、SR-04超声波测距模块用于采集环境管理的数据通过ESP8266 Wi-Fi模块实现与OneNet物联网平台的通信用 SD卡进行数据本地的存储。第二部分系统软件选型设计完成传感器数据采集、校验与滤波处理用FATFS文件系统去实现SD卡的日志记录通过ESP8266模块把数据上传至OneNet平台通过邮箱实现异常报警的自动邮件告警通过手机App实现远程数据可视化与实时状态查看。2.目录参考内容五、项目主要源码5.1.单片机源码5.1.1主程序#include bord.h void boid_level_init(void) { // } uint8_t oled_buffer[32]; extern uint64_t Mytotal_size ; extern uint64_t Myfree_size ; int8_t wifi_error0;int8_t mqtt_error0; uint32_t co2_value; uint32_t JiaQuan_value; uint32_t distance_value; uint32_t sys_10ms; uint8_t value_error; uint8_t distance_error; //阈值设置 uint16_t dh11_humi_max 90; uint16_t dh11_temp_min 5; uint16_t co2_max 1000; uint16_t disance_max 50; uint16_t jiaquan_max 100; void bord_init(void) { debug_init(); led_init(beep_pb1, BEEP_GPIO_Port, BEEP_Pin, 1); led_off(beep_pb1); led_init(led_pc13, LED_GPIO_Port, LED_Pin, 1); led_on(led_pc13); delay_init(); OLED_Init(); OLED_NewFrame(); OLED_PrintString(0, 0, 系统初始化..., font16x16, OLED_COLOR_NORMAL); OLED_PrintString(0, 16, 请等候..., font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); HAL_Delay(500); sdio_fats_init();HAL_Delay(50); OLED_NewFrame(); sprintf((char *)oled_buffer, Total: %llu MB, Mytotal_size / (1024 * 1024) ); OLED_PrintString(0, 0,(char *) oled_buffer, font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer, Free: %llu MB, Myfree_size / (1024 * 1024)); OLED_PrintString(0, 16, (char *) oled_buffer, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); HAL_Delay(1000); co2_init(); led_off(led_pc13); esp8266_rx_init(); OLED_NewFrame(); OLED_PrintString(0, 0, (char *) OneNetConnect..., font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); // if(wifi_init()!0){wifi_error1;} // if(wifi_error1){OLED_PrintString(0, 16,(char *) WIFI_Error!, font16x16, OLED_COLOR_NORMAL);} // else {OLED_PrintString(0, 16,(char *) WIFI_OK!, font16x16, OLED_COLOR_NORMAL);} wifi_init(); OLED_PrintString(0, 16,(char *) WIFI_OK!, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); mqtt_init(); OLED_PrintString(0, 32,(char *) MQTT_OK!, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); HAL_Delay(500); OLED_NewFrame(); cs_init(); } DHT11_Data_TypeDef dh11_temp; DHT11_Data_TypeDef dh11_date; int8_t dh11_date_error0; int8_t CO2_date_error0; int8_t JiaQuan_date_error0; extern uint16_t formaldehyde_concentration ; // 甲醛浓度 (ug/m³) extern uint16_t eco2_concentration ; // 等效eCO2浓度 (PPM) uint8_t count; void bord_loop_task(void) { // 数据显示 sprintf((char *)oled_buffer,温度:%d,dh11_date.temp_int); OLED_PrintString(0, 0, (char *)oled_buffer, font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,湿度:%d,dh11_date.humi_int); OLED_PrintString(64, 0, (char *)oled_buffer, font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,CO2:%d ppm,co2_value); OLED_PrintString(0, 16, (char *)oled_buffer, font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,甲醛:%d ug/m,JiaQuan_value); OLED_PrintString(0, 32, (char *)oled_buffer, font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,距离:%d cm,distance_value); OLED_PrintString(0,48, (char *)oled_buffer, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); // 湿度温度采集 if (DHT11_ReadData(dh11_temp)) { // debug_send(Humi%d RH,Temperture%d℃\r\n, dh11_temp.humi_int, dh11_temp.temp_int); dh11_date dh11_temp; sdio_fats_write(dh11_date.temp_int ,dh11_date.humi_int) ; } // 距离采集distance cs_read(); // debug_send(distance%.2f cm\r\n, distance); if(distance256) { distance_valuelow_pass_filter(distance); } //CO2采集 甲醛测量 co2_valueeco2_concentration; JiaQuan_valueformaldehyde_concentration; // 数据错误处理 if(dh11_date.humi_intdh11_humi_max||dh11_date.temp_intdh11_temp_min){dh11_date_error1;} else{dh11_date_error0;} if(co2_valueco2_max){CO2_date_error1;} else{CO2_date_error0;} if(JiaQuan_valuejiaquan_max){JiaQuan_date_error1;} else{JiaQuan_date_error0;} if(distance_valuedisance_max){distance_error1;} else{distance_error0;} // 数据异常 if(dh11_date_error1||CO2_date_error1|| JiaQuan_date_error1|| distance_error1){value_error1;} if(value_error1) { value_error0; HAL_Delay(1000); OLED_NewFrame(); if(dh11_date_error1) {OLED_PrintString(0,0, (char *)温湿度数据错误, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,0, (char *) , font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,0, (char *)温湿度数据错误, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,0, (char *) , font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); HAL_Delay(1000); } if(CO2_date_error1) {OLED_PrintString(0,16, (char *)二氧化碳数据错误, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,16, (char *) , font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,16, (char *)二氧化碳数据错误, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,16, (char *) , font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); HAL_Delay(1000); } if(JiaQuan_date_error1) {OLED_PrintString(0,32, (char *)甲醛数据错误, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,32, (char *) , font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,32, (char *)甲醛数据错误, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,16, (char *) , font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); HAL_Delay(1000); } if(distance_error1) { OLED_PrintString(0,32, (char *)距离数据错误, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,32, (char *) , font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,32, (char *)距离数据错误, font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); led_on(beep_pb1); HAL_Delay(1000); led_off(beep_pb1); OLED_PrintString(0,16, (char *) , font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); HAL_Delay(1000); } } HAL_Delay(10); sys_10ms; // WIFI任务 if(sys_10ms20) {sys_10ms0; count(count1)%4; switch(count) { case 0:esp8266_connect_send(Humi,dh11_date.humi_int, 100) ; break; case 1:esp8266_connect_send(JiaQuan,JiaQuan_value, 100) ; break; case 2:esp8266_connect_send(CO2,co2_value, 100) ; break; case 3:esp8266_connect_send(Temp,dh11_date.temp_int, 100) ; break; } } }5.1.2 DHT11代码#include dt11.h static void DHT11_PP_OUT(void) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin DHT11_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); } /** * ************************************************************************ * brief 将DHT11配置为上拉输入模式 * ************************************************************************ */ static void DHT11_UP_IN(void) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin DHT11_PIN; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; //上拉 HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); } /** * ************************************************************************ * brief 读取字节 * return temp * ************************************************************************ */ uint8_t DHT11_ReadByte(void) { uint8_t i, temp 0; for (i 0; i 8; i) { while (DHT11_ReadPin 0); // 等待低电平结束 delay_us(40); // 延时 40 微秒 if (DHT11_ReadPin 1) { while (DHT11_ReadPin 1); // 等待高电平结束 temp | (uint8_t)(0X01 (7 - i)); // 先发送高位 } else { temp (uint8_t)~(0X01 (7 - i)); } } return temp; } /** * ************************************************************************ * brief 读取一次数据 * param[in] DHT11_Data 定义的结构体变量 * return 0或1数据校验是否成功 * note 它首先向DHT11发送启动信号然后等待DHT11的应答。如果DHT11正确应答 * 则继续读取湿度整数、湿度小数、温度整数、温度小数和校验和数据 * 并计算校验和以进行数据校验 * ************************************************************************ */ uint8_t DHT11_ReadData(DHT11_Data_TypeDef *DHT11_Data) { DHT11_PP_OUT(); // 主机输出主机拉低 DHT11_PULL_0; HAL_Delay(18); // 延时 18 ms DHT11_PULL_1; // 主机拉高延时 30 us delay_us(30); DHT11_UP_IN(); // 主机输入获取 DHT11 数据 if (DHT11_ReadPin 0) // 收到从机应答 { while (DHT11_ReadPin 0); // 等待从机应答的低电平结束 while (DHT11_ReadPin 1); // 等待从机应答的高电平结束 /*开始接收数据*/ DHT11_Data-humi_int DHT11_ReadByte(); DHT11_Data-humi_dec DHT11_ReadByte(); DHT11_Data-temp_int DHT11_ReadByte(); DHT11_Data-temp_dec DHT11_ReadByte(); DHT11_Data-check_sum DHT11_ReadByte(); DHT11_PP_OUT(); // 读取结束主机拉高 DHT11_PULL_1; // 数据校验 if (DHT11_Data-check_sum DHT11_Data-humi_int DHT11_Data-humi_dec DHT11_Data-temp_int DHT11_Data-temp_dec) { return 1; } else { return 0; } } else // 未收到从机应答 { return 0; } }5.1.3 TVOC-301代码#include co2.h uint8_t co2_rx; // 存储每次中断接收的字节 uint16_t tvoc_concentration 0; // TVOC浓度 (ug/m³) uint16_t formaldehyde_concentration 0; // 甲醛浓度 (ug/m³) uint16_t eco2_concentration 0; // 等效eCO2浓度 (PPM) uint8_t co2_data_ready 0; // 数据解析完成标志 void co2_init(void) { HAL_UART_Receive_IT(huart3, co2_rx, 1); } void co2_it_get(void) { static uint8_t co2_state 0; static uint8_t tvoc_temp[2]; // TVOC高低字节 static uint8_t formaldehyde_temp[2]; // 甲醛高低字节 static uint8_t eco2_temp[2]; // eCO2高低字节 static uint8_t sum 0; // 校验和只保留低8位 switch (co2_state) { case 0: // 等待帧头 0x2C if (co2_rx 0x2C) { sum 0x2C; co2_state 1; } break; case 1: // 等待 0xE4 if (co2_rx 0xE4) { sum 0xE4; co2_state 2; } else { co2_state 0; // 帧头错误重置 } break; case 2: // TVOC 高字节 tvoc_temp[0] co2_rx; sum co2_rx; co2_state 3; break; case 3: // TVOC 低字节 tvoc_temp[1] co2_rx; sum co2_rx; co2_state 4; break; case 4: // 甲醛 高字节 formaldehyde_temp[0] co2_rx; sum co2_rx; co2_state 5; break; case 5: // 甲醛 低字节 formaldehyde_temp[1] co2_rx; sum co2_rx; co2_state 6; break; case 6: // eCO2 高字节 eco2_temp[0] co2_rx; sum co2_rx; co2_state 7; break; case 7: // eCO2 低字节 eco2_temp[1] co2_rx; sum co2_rx; co2_state 8; break; case 8: // 校验和第9字节 sum 0xFF; // 只保留低8位重要 if (sum co2_rx) { // 解析成功 tvoc_concentration (tvoc_temp[0] 8) | tvoc_temp[1]; formaldehyde_concentration (formaldehyde_temp[0] 8) | formaldehyde_temp[1]; eco2_concentration (eco2_temp[0] 8) | eco2_temp[1]; co2_data_ready 1; } else { co2_data_ready 0; // 校验失败 } // 重置状态准备下一帧 co2_state 0; sum 0; break; default: co2_state 0; sum 0; break; } debug_send(%c,co2_rx); HAL_UART_Receive_IT(huart3, co2_rx, 1); // 启动下一次中断接收 }5.1.4 SD卡代码#include sd.h extern uint8_t retSD; /* Return value for SD */ extern char SDPath[4]; /* SD logical drive path */ extern FATFS SDFatFS; /* File system object for SD logical drive */ extern FIL SDFile; /* File object for SD */ uint64_t Mytotal_size ; uint64_t Myfree_size ; /** * brief SD卡FATFS初始化挂载SD卡并打印内存信息 */ void sdio_fats_init() { FRESULT fre; DWORD total_clusters, free_clusters; UINT sector_size; char info_buf[128]; FATFS *p_fs; // 用于接收f_getfree返回的文件系统指针 // 挂载SD卡 fre f_mount(SDFatFS, SDPath, 1); if (fre FR_OK) { HAL_UART_Transmit(huart1, (uint8_t*)SD mount ok\r\n, sizeof(SD mount ok\r\n), 1000); // 获取SD卡内存信息 fre f_getfree(SDPath, free_clusters, p_fs); if (fre FR_OK) { // 从返回的文件系统指针获取参数SD卡标准扇区大小为512字节替代ssize total_clusters (p_fs-n_fatent - 2); // 总簇数减去保留簇 sector_size 512; // SD卡默认扇区大小为512字节修复ssize不存在的问题 uint64_t total_size (uint64_t)total_clusters * p_fs-csize * sector_size; uint64_t free_size (uint64_t)free_clusters * p_fs-csize * sector_size; Mytotal_size total_size; Myfree_sizefree_size ; // 格式化容量为MB sprintf(info_buf, Total: %llu MB, Free: %llu MB\r\n, total_size / (1024 * 1024), free_size / (1024 * 1024)); HAL_UART_Transmit(huart1, (uint8_t*)info_buf, strlen(info_buf), 1000); } else { HAL_UART_Transmit(huart1, (uint8_t*)Get SD info error\r\n, sizeof(Get SD info error\r\n), 1000); } } else { HAL_UART_Transmit(huart1, (uint8_t*)SD mount error\r\n, sizeof(SD mount error\r\n), 1000); } } /** * brief 向date.txt追加写入温湿度数据 * param temp 温度值 * param humi 湿度值 */ void sdio_fats_write(uint32_t temp, uint32_t humi) { FRESULT fre; UINT bw; char data_buf[64]; // 格式化数据%lu适配uint32_t sprintf(data_buf, Temp:%d,Humi:%d\r\n, temp, humi); // 打开文件不存在则创建存在则以写模式打开 fre f_open(SDFile, date.txt, FA_OPEN_ALWAYS | FA_WRITE); if (fre FR_OK) { // 移动文件指针到末尾实现追加效果 f_lseek(SDFile, f_size(SDFile)); // 写入数据 fre f_write(SDFile, data_buf, strlen(data_buf), bw); if (fre FR_OK bw strlen(data_buf)) { HAL_UART_Transmit(huart1, (uint8_t*)Write success\r\n, sizeof(Write success\r\n), 1000); } else { HAL_UART_Transmit(huart1, (uint8_t*)Write error\r\n, sizeof(Write error\r\n), 1000); } // 关闭文件 f_close(SDFile); } else { HAL_UART_Transmit(huart1, (uint8_t*)Open file error\r\n, sizeof(Open file error\r\n), 1000); } }六、工程主要内容工程的具体内容包括但不限于原理图、程序代码、流程图等等。完整工程、原理图、APP 源码我已经开源到 Gitee需要的同学可以评论区留言或者私信004-基于STM32的智能仓库环境监控系统。开源主要内容STM32毕设项目分享基于STM32的智能仓库环境监控系统物联网、FATFS文件操作系统