news 2026/9/16 20:23:47

STM32空气监测系统源码拆解:从ADC采样到数据可视化全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32空气监测系统源码拆解:从ADC采样到数据可视化全流程解析

简介:基于STM32单片机空气监测系统设计毕业设计资料包,主要面向嵌入式、自动化、电子信息等专业的在校学生与开发者,适用于毕业设计、课程设计、项目初期演示及二次开发入门。压缩包内共包含312个文件,整包大小15.32MB,以C语言源码、Keil工程配置、液晶屏驱动和设计文档为主体,同时附带SQL初始化脚本、程序烧写文件以及上位机或小程序界面相关资源,便于从底层驱动到应用层展示进行系统对照,目录层级也较直观,容易定位所需模块。目前已有200人学习,说明其具有一定参考价值。资料基于标准外设库实现,工程中涉及定时器、ADC采集、I2C存储器、USART通信等关键模块,配合ILI9341屏幕完成空气质量监测界面;除主程序源码外,还提供详细设计文档、编译过程文件和图片素材,能够支撑从数据采集、处理到显示的完整链路学习,也可在现有代码基础上替换传感器或修改显示逻辑,直接用于毕设演示与答辩准备,适合从入门到进阶不同阶段的读者。

1. 一套空气监测系统毕设源码,值得拆的不只是能跑

拿到一份STM32单片机毕业设计资源,先别急着ctrl+c和烧录。我翻看了一下源码包结构,adc.cili9341_lcd.ctim.cusart.c这些文件摆在一起,基本就是一套完整的空气数据采集与显示链路:传感器模拟量经ADC采集,TIM定时器负责采样节拍,USART负责和PC上位机通信,LCD屏做人机交互。这类系统在环境监测、智能家居、农业大棚场景里是标准配置,难点不在器件多,而在于时序配合和数据可靠度。适合电子、通信、自动化专业做毕设或课设,也适合刚起步的嵌入式开发者用来理解STM32标准外设库的协作方式。这篇就把这套资源从工程初始化到排错实践完整拆开,每个环节给出可复现的操作。

2. 先看懂源码结构和硬件资源映射,再动手改代码

拿到压缩包先别急着打开Keil编译,花十分钟把源码文件过一遍,能省掉后面大量排查时间。这套项目基于STM32F10x标准外设库,源码文件清单本身就是一份硬件资源占用表。

2.1 从文件名反推系统架构:外设和模块的对应关系

看看压缩包里的.c文件,每一类外设对应一个文件。stm32f10x_rcc.c是时钟控制,stm32f10x_adc.c负责模拟量采集,stm32f10x_tim.c提供定时器做采样节拍控制,stm32f10x_usart.c管理串口通信,stm32f10x_i2c.cstm32f10x_can.c为扩展接口预留空间,ili9341_lcd.cfonts.c构成显示子系统。stm32f10x_flash.c的存在意味着系统大概率支持参数存储功能。

这意味着你在后续做毕设答辩或功能演示时,可以直接说清楚:ADC采集空气质量传感器信号、TIM定时器触发采样、LCD实时刷新数据、USART把数据上传到监控端。这套路径是环境监测设备的通用数据流。理解了这个,你就知道改哪部分会影响什么——改ADC采样公式会影响读数,改TIM预分频值会影响采样频率,改USART波特率会影响上下位机通讯。

2.2 传感器信号链路:从模拟量到可显示的数值

空气监测的核心一环,是弄清楚传感器输出的电信号如何变成LCD屏上的数字。常规空气质量传感器(如MQ系列)输出模拟电压,电压大小与气体浓度呈一定关系,有的模块直接输出0~3.3V,有的则需要经过运算放大器调理到ADC可采范围。STM32F10x的ADC是12位精度,当参考电压为3.3V时,理论分辨率为:

分辨率 = 参考电压 / 2^12 = 3.3V / 4096 ≈ 0.806mV/LSB

这套流程在源码中通常分为三步:配置ADC引脚为模拟输入、设置ADC采样时间和触发方式、在主循环或定时器中断里读取转换结果。源码里的adc.c就是完成这些工作的。你需要特别留意采样时间参数,空气传感器的输出阻抗普遍不低,采样时间取短了,采样电容充不满,读出来的数值会偏低且不稳定。标准外设库中ADC采样时间可配置为1.5~239.5个周期,经验做法是取55.5或71.5个周期,保证源阻抗和采样电容匹配。

提示:现场环境用示波器量传感器输出,读数波动超过±30mV时优先检查ADC采样时间和电源纹波,别急着怀疑传感器坏了。

2.3 定时器触发采样,改成定时采集模式

代码里带了tim.c,说明这个系统很可能是用定时器触发ADC采样,而不是在主循环里用delay延时轮询。两者在工程上的差别很大:前者有固定采样间隔,滤波和平均算法才有意义;后者的采样间隔会被其他业务代码阻塞,数据抖动明显。

用定时器触发ADC的常见做法有两种,一种是直接利用定时器的PWM输出模式,配置成PWM脉冲触发外部ADC,一种是把定时器更新事件连接到ADC外部触发。我以前在类似的监测项目里通常直接配置TIM2为更新中断:

void TIM2_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period = 849; TIM_TimeBaseStructure.TIM_Prescaler = 71; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); }

这段代码把系统72MHz时钟先分频到1MHz(预分频器72-1),再定时(自动重装载值850-1),最终得到约1kHz的中断频率。你在自己的项目里调整采样周期,只要改TIM_PrescalerTIM_Period两个参数即可。公式是中断频率 = 72MHz / (Prescaler+1) / (Period+1)。中断服务函数里置一个标志位,主循环检测标志位后启动ADC转换,这样采样频率和显示刷新频率就解耦了,LCD刷屏慢也不会拖累采样节奏。

3. 从零搭建工程:时钟树、GPIO、串口的初始化顺序有讲究

毕设源码拿到手,最有价值的操作不是直接写业务逻辑,而是完整复现一遍初始化流程。得益于stm32f10x_rcc.cstm32f10x_usart.c这些文件,你可以把每个外设的使能顺序梳理出来。初始化顺序错了会导致外设莫名其妙不工作。

3.1 系统时钟配置:一切外设的前提

STM32F10x上电默认是HSI 8MHz,如果不切换时钟源,串口波特率会算不准,ADC的采样时钟也会偏移,最典型的现象是串口打印乱码、ADC采样值和万用表测量值对不上。源码里大概率通过SystemInit函数配置了外部晶振并倍频到72MHz,也就是常说的高速外部时钟和PLL锁相环路径:

void RCC_Configuration(void) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); if (RCC_WaitForHSEStartUp() == SUCCESS) { RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); RCC_PLLCmd(ENABLE); while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while (RCC_GetSYSCLKSource() != 0x08); } }

RCC_PLLConfig第一参数是HSE分频系数,第二参数是倍频数。外部晶振8MHz,倍频9倍,得到72MHz主频。这里最容易出问题的是外部晶振虚焊或起振电容不匹配,导致RCC_WaitForHSEStartUp()永远等不到成功标志。排查方法也简单:在条件判断里加个死循环灯,能亮说明HSE正常,不能亮优先检查晶振两脚的负载电容和复位电路。

3.2 串口重定向:printf打印调试信息的工程配置

串口调通了,整个项目的调试效率能提升一个档次。源码里的stm32f10x_usart.c实现了串口初始化,在此基础上做printf重定向,就可以在串口助手实时看传感器的ADC原始值和换算后的浓度值。

int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, (uint8_t)ch); return ch; }

这个函数必须在Keil工程里勾选"微库"选项才能生效,不勾选的话编译器会使用半主机模式,printf不会走串口。同时要注意USART1的GPIO复用配置,TX引脚要配成推挽复用输出,RX引脚配成浮空输入。波特率建议直接用115200,传感器数据量不大,不需要更高,频繁改波特率只会增加上下位机不匹配的风险。

3.3 GPIO和I2C/CAN资源的合理分配,留着接口做扩展

GPIO分配是整个项目的接线依据。LCD占用的引脚一般比较多,如果是FSMC接口的屏,直接挂在数据总线上,效率最高;如果是SPI接口,占用管脚少但刷新率低一些。I2C和CAN在空气监测系统里通常不是刚需,但源码中包含了相应驱动文件,说明这套板子预留了扩展能力。I2C总线可以用来挂温湿度传感器如SHT30,CAN总线则常用于工业环境监测组网。你可以在液晶型号不变的前提下,把I2C和CAN部分的初始化代码先注释掉,需要时再按外设手册逐步打开。核心原则是:先屏蔽不用外设,保证最小系统稳定运行,再逐个开启外设做增量开发。

4. ADC采样链路实测:滤波、定标与显示数据联动

初始化跑通后,这个阶段面对的核心问题只有一个:LCD上显示的数字可不可信。ADC采样不是简单的读寄存器,通道电压到最终显示值还要经过采样配置、滤波平滑、定标换算三个环节。这套源码把链路做得很完整,逐段拆开才能在里面加自己的逻辑。

4.1 单通道ADC的连续采样与DMA搬运

源码中stm32f10x_adc.c大概率做了ADC初始化,但采样结果是用查询方式读还是用DMA方式搬,直接决定了数据质量。用查询方式读ADC时,如果主循环里恰好有延时或LCD刷新操作,采样间隔就会不均匀,数据容易出现周期性波动。用DMA搬运则能确保采样间隔不受主循环影响,所以我在做类似项目的时候通常直接用DMA方式:

void ADC_DMA_Init(void) { DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adc_value; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 1; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); ADC_SoftwareStartConvCmd(ADC1, ENABLE); DMA_Cmd(DMA1_Channel1, ENABLE); }

这段配置的关键有两点,一是把DMA模式设置为循环模式,ADC转换结果会不停写入adc_value变量,无需CPU干预,适合对实时性要求较高的采集场景;二是开启了ADC的软件连续转换模式,DMA搬完一次会自动启动下一次转换。记得在stm32f10x_adc.c初始化的基础上做修改时保留原有的校准流程,否则首次上电读数会系统性偏低。ADC连续转换模式下CPU占用率几乎为零,剩下的算力可以全部留给LCD刷新和业务逻辑。

4.2 中值滤波和滑动平均的实际取舍,一组实测参数供参考

空气质量传感器输出的模拟量天然有噪声,风机转动、背景气体干扰都可能反映在ADC数值上。不做滤波直接显示,LCD屏上的数字会在末位跳动,观感很差,上位机画曲线也是一团毛刺。常用滤波方案就两种:中值滤波适合剔除偶发尖峰,滑动平均适合平滑连续波动。

我一般把两种结合起来:先做5点中值滤波剔除脏数据,再做16点滑动平均输出最终显示值:

#define FILTER_N 5 #define SMOOTH_N 16 uint16_t middle_filter(uint16_t *buf, uint8_t len) { uint8_t i, j; uint16_t temp; for (i = 0; i < len - 1; i++) for (j = i + 1; j < len; j++) if (buf[i] > buf[j]) { temp = buf[i]; buf[i] = buf[j]; buf[j] = temp; } return buf[len / 2]; }

滑动平均部分不需要额外开大数组,用公式递推计算即可:smooth_value += (new_value - smooth_value) / SMOOTH_N。这个算法等效于一阶低通滤波,参数越小越平滑,响应越慢。做空气监测时,气体浓度变化本身就是慢变量,可以放心把响应时间控制在一两秒以内。在验证这些滤波代码有效性时,可以用一只100Ω电阻把ADC输入端短接到地,此时理想读数为0;再用干净的直流稳压电源供一个稳定电压,给滤波器一组带毛刺的原始数据,看输出是不是在期望值附近徘徊。

注意:中值滤波的排序代码会消耗微秒级时间,如果放在主循环里会对整体时序有轻微影响,但做空气监测完全够用,不必为此占用定时器中断。

4.3 电压到浓度的换算,需要看传感器数据手册的曲线

ADC寄存器读出的是0~4095的数字量,必须经过两级换算才能变成可读的浓度值。第一步把数字量换算成电压:

float voltage = (float)adc_value * 3.3f / 4096.0f;

第二步是把电压换算成浓度。这一步没有统一公式,不同传感器的响应曲线差异极大。以MQ系列为例,它在双对数坐标系下近似为线性,需要用两个标准气体浓度点标定出斜率。源码里可能直接用了线性近似concentration = voltage * scale - offset,这在浓度变化范围小、精度要求不高的场景下可以接受。如果要做严谨的定标,我的经验是用标准气体配置三个浓度点,记录对应的电压值,再用最小二乘法拟合曲线参数,最后把拟合公式固化到固件里:

float a = 1.02f, b = -0.87f; // 示例斜率与截距 float concentration = powf(10.0f, a * log10f(voltage) + b);

这里原代码的换算系数只适用于它配套的传感器模块。如果你在实验室拿到了不同品牌的传感器,务必核对数据手册或与实际仪表对比量程。刚上手时,一个稳妥的验证方法是把传感器放在窗外通风处,先记录30分钟数据,大致估算一个干净空气的电压基线。基线偏了,传感器标定或滤波参数就有问题。

4.4 超标报警阈值如何写到Flash里存储

监测系统不能只显示数据,还要有点报警能力。源码中的stm32f10x_flash.c就是干这个的。把报警阈值存到Flash里,断电不丢失,下次开机免重设。我通常在STM32片内Flash的最后一页存配置参数,因为最后一页地址固定。写之前必须擦除整页,擦除和写入期间不能掉电,否则会损坏Flash内容。读出来先用一个魔数校验有效性,比如0xA5A5

#define FLASH_CONFIG_ADDR 0x0803FC00 #define CONFIG_MAGIC 0xA5A5 typedef struct { uint16_t magic; uint16_t alarm_threshold; } config_t; void config_save(config_t *cfg) { FLASH_Unlock(); FLASH_ErasePage(FLASH_CONFIG_ADDR); FLASH_ProgramHalfWord(FLASH_CONFIG_ADDR, cfg->magic); FLASH_ProgramHalfWord(FLASH_CONFIG_ADDR + 2, cfg->alarm_threshold); FLASH_Lock(); }

存储时注意写入阈值要以原始电压值还是换算后浓度值保存,源码在实际工程中建议保存为电压值,这样无论前台怎么调换算公式,阈值依然有参考意义。这部分的调试技巧是:用串口命令行实现阈值动态设置,避免每次改阈值都要重新烧录整包程序,尤其适合毕设答辩现场演示。

5. 把监测数据跑起来:LCD显示、串口上传和上位机对接

当ADC数据链路稳定后,可以开始做数据出口。ILI9341液晶屏用于现场显示,USART串口用于把数据发往电脑端。这两个出口在毕设答辩中缺一不可,LCD展示系统完整性,串口数据展示可联网性。

5.1 ILI9341显示驱动的接入细节,带ili9341_lcd.cfonts.c的配合方式

ILI9341是典型的TFT-LCD驱动芯片,分辨率常见240x320,接口有SPI和FSMC两种。源码包里既然有ili9341_lcd.c,那板子上一定有对应物理接口,通常是SPI。初始化LCD之前要先确认硬件复位引脚的电平时序:拉低至少10us再拉高,等待5ms后再发初始化命令,顺序搞反会出现屏能亮但花屏或白屏的典型问题。

fonts.c提供了字库,字符显示函数的本质是把每个字符的字模数据写进GRAM的指定区域。字模数组一维是字符的ASCII码索引,每个元素代表一列的位图数据。理解了位深和扫描方向后,要做大字号显示时只需自己用取模软件生成新的字模数组。刷新部分建议用区域填充:

void ILI9341_ShowString(uint16_t x, uint16_t y, const char *str) { while (*str) { if (x > 240 - 16) { x = 0; y += 16; } if (y > 320 - 16) { y = 0; } ILI9341_ShowChar(x, y, *str++); x += 16; } }

这套显示逻辑改起来很简单,例如要改显示位置就把传入的XY坐标改成你需要的坐标值,例如屏幕中间显示当前浓度。注意35MB以上位图直接调用会占用大片连续缓冲区,嵌入式环境里,建议先裁剪成小尺寸,再分块搬运到GRAM。

5.2 帧格式设计:让串口上传的数据可以直接进Excel或LabVIEW

把监测数据通过USART上传到电脑时,不要用散乱的printf("%d\r\n", value),这种格式给上位机解析制造麻烦。建议设计一个固定帧格式,帧头+数据+校验,方便PC端用任意串口工具记录:

uint8_t tx_buf[8]; tx_buf[0] = 0xAA; tx_buf[1] = 0x55; tx_buf[2] = (uint8_t)(adc_value >> 8); tx_buf[3] = (uint8_t)(adc_value & 0xFF); tx_buf[4] = (uint8_t)(voltage * 100.0f); tx_buf[5] = (uint8_t)(concentration * 100.0f); tx_buf[6] = (uint8_t)(alarm_flag); tx_buf[7] = (uint8_t)(tx_buf[0] ^ tx_buf[1] ^ tx_buf[2] ^ tx_buf[3] ^ tx_buf[4] ^ tx_buf[5] ^ tx_buf[6]);

为什么用0xAA0x55做双帧头?这两个字节二进制是10101010和01010101,在串口上出现概率低,且具有翻转特性,能有效减少伪帧同步。用异或做单字节校验足以应对偶发误码,如果你想进一步增强可靠性,可以换成CRC8。波特率设置成115200,8位数据位、1位停止位、无校验,这个参数组合是绝大多数USB转串口工具的默认配置。电脑端用SSCOM或VOFA+接收,在标签页直接按十六进制显示,就能看到符合帧结构的数据。如果你打算用LabVIEW或Python对接,把帧格式定义写到文档里,上位机解析就很简单了。

5.3 帧数据稳定性调试,踩过最常见的两个坑

串口数据不稳,最常见的是供电问题而非代码问题。当你用USB线直接给开发板供电时,电机、继电器这类感性负载一动作,USB口电压就被拉低,STM32和LCD同时掉电重启。传感器信号线也会受到电源噪声干扰。解决方式分两级:入门做法是给传感器单独用AMS1117-3.3或LM2596降压模块供电,并保证它与主控板共地;进阶做法是用示波器看3.3V纹波,若在ADC采样时段纹波超过50mV,则需要在传感器电源入口处加LC滤波。这个坑在实验室调试阶段就暴露出一次,可以让你的毕设答辩时被评委问到时都有话讲。

6. 环境监测系统进阶:Modbus协议对接与数据可视化验证

基础链路只剩下最后一环:把整套系统接入实际业务场景。以这套STM32空气监测硬件为基础,有两个高级玩法值得直接在毕设答辩中起到加分效果,一是引入Modbus协议与组态软件或PLC通信,适合工业环境监测场景;二是通过串口把数据送进Python脚本,实时绘图并落盘记录。

6.1 移植一个精简Modbus从站协议,通信格式与寄存器映射

Modbus RTU是工业环境监测领域事实上的通信标准。你完全可以在这套系统上自己写一个精简版的Modbus从站:用USART接收中断,把收到的指令存入缓冲区,主循环解析指令并生成响应。以功能码0x03(读保持寄存器)为例,把ADC原始值放到寄存器地址0x0000,报警阈值放到0x0001,对应代码如下:

寄存器地址内容读写属性
0x0000当前ADC采样值只读
0x0001当前浓度换算值只读
0x0002报警阈值读写
uint8_t modbus_buf[64]; uint16_t reg_hold[4] = {0, 0, 1000, 0}; void Modbus_Parse(void) { uint16_t start_reg, reg_count, i; uint8_t crc_l, crc_h, n; if (modbus_len < 8) return; if (modbus_buf[0] != 0x01) return; if (modbus_buf[1] == 0x03) { start_reg = (modbus_buf[2] << 8) | modbus_buf[3]; reg_count = (modbus_buf[4] << 8) | modbus_buf[5]; n = reg_count * 2; modbus_buf[2] = n; for (i = 0; i < reg_count; i++) { modbus_buf[3 + i * 2] = (uint8_t)(reg_hold[start_reg + i] >> 8); modbus_buf[4 + i * 2] = (uint8_t)(reg_hold[start_reg + i] & 0xFF); } crc_l = ...; crc_h = ...; modbus_buf[3 + n] = crc_l; modbus_buf[4 + n] = crc_h; USART_Send(modbus_buf, 5 + n); } }

帧解析逻辑重点在于CRC16校验,不要省。Modbus主站工具(如Modbus Poll)连接后如果不能正常通信,优先检查串口参数是否为9600-8-N-1以及寄存器地址是否越界。市面上很多USB转485模块没有自动收发切换,在收发切换调试时建议先跑一个自发自收的测试帧,确认RS485的收发方向控制引脚与串口的RTS联动配置正确。

6.2 用Python做一个实时监控面板,验证你整条数据链路是否正确

如果你更倾向于走物联网方向,可以用Python写一个几十行的脚本,用pyserial读取这套STM32系统上报的数据,再用matplotlib生成滚动曲线。这样整个系统就有了数据可视化闭环:

import serial import matplotlib.pyplot as plt ser = serial.Serial('COM5', 115200, timeout=1) buf = [] while len(buf) < 100: line = ser.readline() adc_val = int(line.strip()) buf.append(adc_val) plt.plot(buf) plt.xlabel('Sample Point') plt.ylabel('ADC Value') plt.title('Air Monitor Real-time Curve') plt.show()

这个脚本的注意点在于,STM32端要以文本行方式输出原始ADC值,结尾带\r\n,Python端readline才能正确按行分割。数据链路中出现连续掉点或乱码,多数情况是串口缓冲区和缓存发送的波特率不一致,或者STM32端printf发送与主循环刷新频率不匹配导致缓冲区覆盖。这一节对应到空气质量监测的完整玩法是:先用这套毕设源码把基础硬件跑通,再根据自己的方向选择Modbus工业模式或Python可视化模式进行毕业设计功能创新,比单纯搬运现成源码的答辩效果会高出一个层级。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 20:22:54

大数据架构降本实践:从自建HBase/Redis迁移到Lindorm+Tair,成本降60%

1. 项目背景&#xff1a;为什么大数据架构一定要动刀降本先说个现实问题。我们团队之前维护的大数据平台&#xff0c;承担着全公司用户行为日志、订单流水、风控特征、推荐召回等核心链路的读写。高峰期日增数据量在几十TB级别&#xff0c;总存储量奔着PB去&#xff0c;组件也越…

作者头像 李华
网站建设 2026/9/16 20:22:37

Python构建招标数据采集系统:从网页解析到结构化存储

1. 项目背景与核心价值公共资源交易平台的招标数据是企业市场情报的黄金矿脉。作为一名长期从事数据采集与分析的老兵&#xff0c;我见过太多企业因为信息滞后错失商机。这个项目将带你用Python构建完整的招标数据采集系统&#xff0c;从网页解析到结构化存储&#xff0c;实现企…

作者头像 李华
网站建设 2026/9/16 20:22:20

VLC将RTSP转为HTTP视频流:浏览器监控预览实战

1. 为什么浏览器就是不肯直接吃RTSP这口饭搞过安防监控或者可视化大屏的人&#xff0c;大概率都被同一个问题卡过&#xff1a;摄像头在局域网里吐出来的是RTSP流&#xff0c;VLC、ffplay这些桌面播放器点开就能看&#xff0c;可一旦要把画面塞进浏览器页面&#xff0c;浏览器立…

作者头像 李华
网站建设 2026/9/16 20:22:20

轻量级CRM系统开发实战:DeskcommCRM从0到1

做客户管理这件事&#xff0c;很多人一开始是拿Excel凑合的&#xff0c;客户少的时候没问题&#xff0c;等客户超过一两百个&#xff0c;你会发现漏跟进、记错人、翻聊天记录翻到眼瞎&#xff0c;数据散在微信、邮件、通话记录里&#xff0c;谁跟了什么单&#xff0c;完全靠脑子…

作者头像 李华
网站建设 2026/9/16 20:21:56

Python跨平台HID设备直读直写:无需驱动与提权

简介&#xff1a;这是一份面向嵌入式开发与Python自动化测试初学者的跨平台HID设备控制脚本集&#xff0c;解决Linux&#xff08;Ubuntu&#xff09;和Windows环境下Python直接读写USB HID设备的实操难题。资源包含4个文件&#xff08;3个Python脚本1份说明文档&#xff09;&am…

作者头像 李华