十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32环境质量监测系统开源:DHT11+MQ-135代码原理图仿真全解析

STM32环境质量监测系统开源:DHT11+MQ-135代码原理图仿真全解析 很多人第一次做“环境质量监测系统”最后做出的东西其实就是个电子温湿度计——屏幕上显示一行温度、一行湿度再加个笑脸图标总感觉差了点什么。不是温湿度不重要而是“环境质量”这四个字的信息量远不止这些空气里有没有异味、烟气、甲醛湿度是让人觉得闷还是干爽温度波动会不会触发报警这些才是真正影响居住体验的指标。这个开源项目就是冲着“完整”去做的。主控用STM32F103C8T6传感器选了DHT11和MQ-135分别负责温湿度和空气污染程度的采集外加一块0.96寸OLED做本地显示一个蜂鸣器做超限报警。资料包里不是丢给你一堆main.c就完事而是把代码、原理图、Proteus仿真三件套都整理好了从画原理图到写驱动再到仿真验证一整条链路都能跟着走一遍。如果你正准备入坑STM32或者想做一个能放进宿舍/办公室的实用小玩意儿这套资料能省下不少自己瞎折腾的时间。1. 一个“环境质量监测系统”到底该测什么参数传感器选型是整个项目的起点也是很多人最容易纠结的地方。说句大实话市面上能检测环境质量的传感器太多了温湿度有DHT11、DHT22、SHT30空气质量有MQ-135、SGP30、PMS5003粉尘传感器每个看着都挺厉害但要是全往板子上堆成本和调试复杂度都会失控。1.1 为什么是DHT11 MQ-135的组合DHT11这个传感器被很多人嫌弃精度不够但它在这个项目里其实够用。它的湿度测量范围是20%到90%RH精度±5%RH温度范围0到50℃精度±2℃。放在宿舍、办公室这种场景里这个精度完全能反映环境变化趋势。而且它用的是单总线协议一根线就能把数据和主控通信对新手来说也好理解。MQ-135就比较有意思了它是个气体传感器能检测氨气、氮氧化物、酒精、苯、烟雾、二氧化碳这类东西。它的输出方式有两种一个是模拟量输出电压随着气体浓度变化一个是数字量输出超过阈值就输出高低电平。实际项目里一定要用模拟量输出因为这样可以在代码里做阈值判断和趋势分析而不是只知道“超标/没超标”这种二值结果。选这两个传感器还有一个原因它们代表了两种完全不同的数据采集方式——DHT11走的是单总线数字通信MQ-135走的是ADC模拟采样。一个项目把这两种最常见的采集方式都覆盖了后面再做其他传感器项目思路是通用的。1.2 报警阈值不能拍脑袋定报警功能是这个项目的灵魂但阈值设置得合理不合理直接影响使用体验。我之前见过有人把温度报警阈值设成40℃结果夏天屋里稍微闷一点就开始狂响半夜能把人吵醒。结合GB/T 18883《室内空气质量标准》和实际体感推荐这套初始阈值监测项报警条件说明温度≥30℃ 或 ≤10℃超出人体舒适区湿度≥70%RH 或 ≤30%RH过湿容易滋生霉菌过干容易口干舌燥空气质量ADC值 基准值×1.3采用相对变化量而非绝对电压值那个MQ-135的阈值要单独说一下。这传感器有个特性就是同一浓度下不同批次、不同环境下测出来的电压值都不一样所以不能定一个死板的ADC阈值。正确做法是系统上电后先预热几分钟把这段时间的稳定读数记下来当基准值然后在实际运行中跟基准值做比例判断。这样换一个环境、换一块板子阈值都不用重新改。1.3 传感器选型时最容易被忽略的事MQ-135是需要预热的而且预热时间不短。刚上电那几分钟它的输出值会剧烈漂移如果你这时候做报警判断大概率会误报。所以代码里得加一个“开机静默期”比如上电后前5分钟只采集、不报警等传感器稳定了再开始正常工作。另外DHT11的采样间隔不能太短数据手册上写着采集周期要大于1秒。这个不是随便写的因为DHT11内部测量一次需要时间你频繁读取它根本来不及完成一次完整的温湿度测量返回的数据自然不可靠。所以主循环里1秒采集一次就对了。2. 原理图的关键节点传感器接口、电源和报警电路怎么连原理图这部分我用的是立创EDA画的因为它有网页版不用装大体积客户端画完还能直接生成BOM和PCB对开源项目来说分享起来也很方便。画图之前先梳理清楚整个系统的供电关系和信号流向这个我放在最前面。2.1 供电架构3.3V和5V要分开走系统供电从USB 5V进来然后分两路一路直接给MQ-135的加热电阻和蜂鸣器供电另一路经过AMS1117-3.3稳压芯片降到3.3V给STM32、DHT11和OLED供电。很多人会问DHT11和OLED不都是3.3V到5V都能供电吗直接统一用5V多省事。这里有个坑STM32的GPIO输出高电平是3.3V如果你给DHT11的VCC接5V那它数据线的电平逻辑也是围绕5V的虽然实际用起来经常没事但严格来说这就属于电平不匹配。为了稳妥DHT11的VCC直接接3.3V这样数据线电平天然匹配不用额外加电平转换电路。AMS1117-3.3的输入输出都要加滤波电容输入端10μF电解电容加100nF瓷片电容输出端也是10μF加100nF要尽量靠近芯片引脚摆放。这个不是玄学稳压芯片距离负载太远或者滤波电容缺失高速数字电路切换时会产生电压跌落轻则ADC采样值跳动重则程序跑飞。2.2 STM32最小系统的几个必备元件如果用的是市面上那种蓝色Pill板最小系统已经帮你画好了你只需要关心外设接口。但如果你打算自己做核心板这几个地方不能省8MHz晶振加两个20pF负载电容给HSE提供时钟源NRST复位引脚接10kΩ上拉电阻到3.3V再并联一个100nF电容到GNDBOOT0引脚通过10kΩ下拉到GND确保从Flash启动VDDA引脚接3.3V并且用10Ω电阻加1μF电容做一个简单的RC滤波这组模拟电源给ADC用VDDA这个引脚特别容易被忽略。STM32的ADC参考电压就是VDDA如果VDDA上直接就是数字电源的3.3V数字电路翻转时产生的噪声全部会耦合进ADC采样结果。加上RC滤波后ADC采样值的稳定性会明显改善。2.3 传感器接口电路的细节DHT11三根线VCC接3.3VGND接地DATA接PB0。数据线上要接一个4.7kΩ上拉电阻到3.3V。原因很简单单总线协议在空闲状态下要求总线是高电平DHT11是通过拉低总线来发起通信的没有上拉电阻总线电平就是浮空的通信大概率失败。MQ-135四根线里需要关注的是AOUT模拟输出和DOUT数字输出。AOUT接PA0也就是ADC1的通道0。DOUT可以留着一个排针不接或者接一个GPIO做个硬阈值备份但主逻辑还是走ADC。MQ-135模块上一般已经焊好了负载电阻和比较器电路所以直接接就能用。要注意它的VCC必须接5V因为内部加热电阻需要5V供电才能达到工作温度接3.3V的话传感器灵敏度会大幅下降。OLED用I2C接口SCL接PB6SDA接PB7这两根线上也需要4.7kΩ上拉电阻。0.96寸的SSD1306模块I2C地址默认是0x3C驱动代码里要用对。2.4 蜂鸣器驱动电路为什么要加三极管蜂鸣器这个细节最能看出一个人画原理图的基本功。直接把蜂鸣器接到GPIO上听起来很省事但STM32的GPIO最大输出电流只有几毫安而一个5V有源蜂鸣器工作电流一般要20到30毫安直接接GPIO根本带不动声音会很小甚至不响。正确的接法是GPIO经过一个1kΩ限流电阻接到S8050三极管的基极三极管发射极接地集电极接蜂鸣器负极蜂鸣器正极接5V。GPIO输出高电平时三极管导通蜂鸣器形成回路发声。蜂鸣器两端还要反向并联一个1N4148二极管防止断电瞬间产生的反向电动势损坏三极管。ER Arduino代码 Proteus仿真的结合3. 固件代码的主干从GPIO初始化到DHT11时序和ADC滤波开发环境用的是Keil MDK5芯片支持包装STM32F103系列。工程建好之后代码按模块拆分成几个文件dht11.c负责温湿度采集mq135.c负责ADC采样和滤波oled.c负责显示buzzer.c负责报警控制main.c负责整体调度。这样拆的好处是每个文件职责单一后面想替换传感器或者加新功能不会牵一发动全身。3.1 DHT11的单总线时序为什么老是读失败DHT11的通信协议看着简单但很多人写起来都在这里卡住。完整的一次读取过程是这样主机先把数据线拉低至少18毫秒然后释放这时候DHT11检测到起始信号会先拉低80微秒表示响应再拉高80微秒准备发送数据。接着就是40位数据每一位都以50微秒的低电平开始如果后面跟的高电平持续约26到28微秒那这一位就是0如果高电平持续约70微秒那这一位就是1。关键点在延时精度上。STM32主频72MHz几条NOP指令就是几百纳秒的事情但DHT11要求微秒级延时。用软件空循环实现微秒延时要特别注意编译器优化开优化后循环可能被优化掉延时时间完全不对。所以要么用SysTick做微秒延时要么在延时函数里用一个volatile变量来防止被优化。读数据时还要注意读取过程中尽量不要被中断打断。因为DHT11的时序非常敏感一位数据的时长也就几十微秒中断服务程序稍微执行久一点就可能错过电平跳变。我在实测中发现如果中断服务函数里有个几微秒的操作DHT11偶发读取失败的概率会明显上升。解决办法是读取DHT11之前关中断读完再打开或者把DHT11的读取放在没有频繁中断的时间片上。3.2 DHT11核心读取代码uint8_t DHT11_ReadByte(void) { uint8_t i, data 0; for (i 0; i 8; i) { while (DHT11_PIN_READ() 0); // 等待50us低电平结束 delay_us(30); // 拉高后30us采样判断 if (DHT11_PIN_READ() 1) { data | (1 (7 - i)); // 高电平持续超过30us判定为1 } while (DHT11_PIN_READ() 1); // 等待高电平结束 } return data; } uint8_t DHT11_Read(float *temp, float *humi) { uint8_t data[5] {0}; DHT11_GPIO_OUT(); // 设置为输出模式 DHT11_PIN_WRITE(0); // 主机拉低总线 delay_ms(20); // 至少保持18ms DHT11_PIN_WRITE(1); // 释放总线 delay_us(30); // 释放后等待DHT11响应 DHT11_GPIO_IN(); // 切换为输入模式 if (DHT11_PIN_READ() 0) // 检测响应信号 { while (DHT11_PIN_READ() 0); // 等待80us低电平结束 while (DHT11_PIN_READ() 1); // 等待80us高电平结束 for (uint8_t i 0; i 5; i) { data[i] DHT11_ReadByte(); } if (data[4] (data[0] data[1] data[2] data[3])) { *humi data[0] data[1] / 10.0f; *temp data[2] data[3] / 10.0f; return 0; // 校验成功 } } return 1; // 读取失败 }这段代码里的校验和判断很重要。DHT11返回40位数据前8位湿度整数、8位湿度小数、8位温度整数、8位温度小数、最后8位校验和。如果前4个字节相加不等于校验和说明这次通信有问题数据直接丢弃不要拿半截数据去显示和报警。3.3 MQ-135的ADC采样直接用原始值就是给自己挖坑MQ-135接在PA0上用ADC1的通道0采集。STM32的ADC是12位的也就是说输入电压0到3.3V对应采样值0到4095。如果直接把ADC寄存器读出来的值拿来判断空气质量你会发现它在不停地上下跳跟股市一样刺激。跳动的来源有两方面一是传感器本身在持续测量气体分子吸附和脱附是个动态过程输出值本身就有微小波动二是ADC采样不可避免地会叠加噪声。所以必须做软件滤波。我用的方案是滑动平均加中值滤波结合连续采集32次去掉最大值和最小值剩下的取平均。32次采样的时间也就几毫秒对1秒一次的采集周期来说完全不影响实时性。#define ADC_SAMPLE_NUM 32 uint16_t GetMQ135Value(void) { uint16_t adc_values[ADC_SAMPLE_NUM]; uint32_t sum 0; uint16_t min_val 4095, max_val 0; for (uint8_t i 0; i ADC_SAMPLE_NUM; i) { adc_values[i] ADC_GetSingleValue(); if (adc_values[i] min_val) min_val adc_values[i]; if (adc_values[i] max_val) max_val adc_values[i]; sum adc_values[i]; } sum - min_val max_val; return (uint16_t)(sum / (ADC_SAMPLE_NUM - 2)); }这个滤波函数就是第1章里提到的“相对变化量报警”的地基。用滤波后的值除以开机预热阶段记录的基准值得到的就是一个相对浓度比例拿它跟1.3这个阈值比较比直接用原始值靠谱得多。关于ADC的配置还有一个容易被忽略的地方ADC的采样时间。如果采样时间太短内部的采样电容还没来得及充电完成就开始转换结果会有偏差。我建议把ADC采样周期配置为55.5个时钟周期以上在72MHz的系统时钟下大概是770ns这样读出来的值才稳定。3.4 主循环的调度方式主程序用一个简单的标志位轮询方式不用上操作系统也不用复杂的状态机volatile uint8_t flag_1s 0; volatile uint8_t flag_100ms 0; void SysTick_Handler(void) { static uint16_t tick_1s 0; static uint16_t tick_100ms 0; if (tick_100ms 100) { tick_100ms 0; flag_100ms 1; } if (tick_1s 1000) { tick_1s 0; flag_1s 1; } } int main(void) { SystemInit(); GPIO_Config(); ADC1_Config(); I2C_Config(); OLED_Init(); delay_init(72); // 开机静默预热期间采集基准值 while (startup_warmup 300) // 5分钟预热 { if (flag_1s) { flag_1s 0; mq135_baseline GetMQ135Value(); startup_warmup; } } while (1) { if (flag_1s) { flag_1s 0; DHT11_Read(temp, humi); mq135_value GetMQ135Value(); Alarm_Check(); } if (flag_100ms) { flag_100ms 0; OLED_Refresh(); } } }为什么显示刷新要100ms一次而传感器采集1秒一次因为OLED刷新一屏数据虽然不算重但全速刷新会占用不少CPU时间1秒刷新又显得迟钝。100ms刷新一次足够流畅又不影响主循环的其他操作。4. 仿真文件的价值在Proteus里先把逻辑跑通再碰真板子你可能觉得仿真就是“画饼”实际项目里一点用没有。这种想法对但也不全对。对于刚入门STM32的人来说仿真最大的价值不是代替真实硬件而是能够在动手焊板子之前先把代码逻辑验证一遍。尤其是DHT11的时序和OLED显示这两块逻辑错了在仿真里一眼就能看出来不用拿着万用表到处找问题。4.1 Proteus仿真环境怎么搭Proteus 8以上的版本都支持STM32F103C8T6可以直接在“Pick Devices”里搜到。搭建步骤很简单新建工程在原理图编辑区放一个STM32F103C8T6放一个DHT11传感器模型DATA引脚接PB0放一个虚拟终端Virtual TerminalRX接PA9、TX接PA10用来查看串口打印的温湿度数据如果用了OLED仿真库里也有SSD1306模型没有的话先用虚拟终端打印代替功能验证是一样的双击STM32芯片在“Program File”里加载Keil编译出来的hex文件晶振频率设成8MHz因为代码里是按8MHz外部晶振配置的然后点运行就能看到虚拟终端上不断刷新温湿度数据。这里有一个很实用的技巧MQ-135的模拟量输入在Proteus里可以用信号发生器模拟。把PA0引脚接到一个直流电压源或者信号发生器上手动调节输出电压就能模拟出“空气质量变化”的场景从而验证ADC采样和报警逻辑有没有写对。这比在真板子上拿打火机去烤传感器要安全得多也精准得多。4.2 Keil和Proteus联调时的两个小坑第一个坑是hex文件路径。Keil默认把编译产物放在工程目录下的Output文件夹里文件名叫工程名.hex但如果你没有在Options for Target里勾选“Create HEX File”编译是不会生成hex文件的。很多人第一次联调时发现找不到hex文件就是这里没勾选。第二个坑是虚拟终端的波特率要跟代码里串口初始化的配置一致。代码里如果用115200初始化USART1虚拟终端属性里的Baud Rate也要设成115200否则打印出来的全是乱码。4.3 仿真的边界在哪里当然也得说清楚Proteus仿真终究是软件模拟。DHT11在仿真里是理想模型电平翻转的时间非常精确不存在真实硬件上那种几十微秒的抖动。所以你在仿真里把所有功能都跑通了不代表真板子上也一定全通——仿真通过只能说明逻辑层面没问题电气层面的事情还是得靠真硬件去验证。所以在项目里我的态度是仿真文件是给使用者建立信心的第一道关卡代码逻辑有没有写离谱了烧录之前先跑一下仿真十分钟就能发现。而真正的时序微调、ADC滤波参数整定还是得在真板子上用串口打印来看波形和数据。5. 开源包的使用路径代码、原理图、仿真怎么配合着看开源项目能不能让人快速上手其实不取决于代码写得多漂亮而是取决于文件结构和文档组织。我把这套环境质量监测系统的资料整理成了下面的结构STM32_EnvMonitor/ ├── 0_Documents/ │ ├── README.md # 项目说明、功能列表、配置方法 │ ├── PinMapping.md # 引脚连接对照表 │ └── ThresholdConfig.md # 报警阈值配置说明 ├── 1_Hardware/ │ ├── Schematic_Source/ # 立创EDA原理图源文件 │ ├── Schematic_PDF/ # 原理图PDF导出版 │ └── BOM.csv # 物料清单 ├── 2_Firmware/ │ ├── MDK-ARM/ # Keil工程文件 │ ├── Core/ # 核心驱动源码 │ └── README.md # 代码结构说明 ├── 3_Simulation/ │ └── EnvMonitor_sim.pdsprj # Proteus仿真工程 └── LICENSE # 开源许可协议5.1 拿到开源包后推荐的行动顺序如果你是照着这个项目学习最好的路径不是上来就打开main.c从第一行读到最后一行的而是按下面这个顺序来先看0_Documents里的PinMapping.md把STM32引脚和传感器的对应关系在脑子里有个印象。这一步花五分钟但能让你后面看代码的时候不迷茫。再看1_Hardware里的原理图PDF不用每个元件都研究重点看电源怎么走、传感器接口接了哪个引脚、上拉电阻有没有接。图纸跟引脚表对照着看整个系统的硬件图景就建立起来了。然后打开3_Simulation里的Proteus工程加载hex跑一下看虚拟终端输出的数据正不正常。这里你就知道了软件逻辑本身是通的。最后才打开Keil工程去改代码改完编译生成新的hex替换到Proteus里验证修改有没有问题。确认没问题了再烧录到真板子上。这个顺序的核心逻辑是“从外部到内部、从验证到修改”。先建立整体认知再做局部修改。很多人拿到开源项目就急着改代码最后往往因为对硬件连接不清楚改了半天也不知道自己的代码是在跟哪个引脚通信。5.2 三个配套文件之间的关系代码、原理图、仿真这三个东西是互相印证的。原理图告诉你某个传感器接到了哪个引脚代码里对应的GPIO配置就必须跟它一致仿真工程验证的是代码逻辑和传感器模型之间的交互而真实硬件验证的则是代码和物理世界之间的交互。这就是为什么我把三个文件放在一起开源出来而不是只丢一个keil工程。对学习者来说看代码遇到疑问可以去查原理图和仿真三条线索互相补充能避开“对着代码瞎猜”的痛苦。5.3 给开源维护者的一些建议如果你也准备把自己的STM32项目开源出去有几点经验是踩过坑之后才学到的一是BOM清单要包含封装信息。别人照着你PCB的封装去买元件如果BOM里只写了“电阻 10kΩ”没写封装对方买到插件电阻而你板子上画的是贴片封装那就尴尬了。二是代码里要写清楚时钟配置。STM32的工程从标准库到HAL库从8MHz外部晶振到内部HSI配置千差万别。我见过太多人在别人的工程基础上改板子结果就是因为时钟初始化部分没看懂导致串口波特率偏了一半。三是README里要把烧录工具和烧录步骤写明白。是用ST-Link还是用串口ISP需不需要BOOT0跳线下载接线怎么接写清楚能帮使用者省下来半个晚上。6. 从能跑到跑稳过程中遇到的坑和解决办法这部分是整套项目里含金量最高的地方。仿真通过了板子也到了看似万事大吉实际调试的时候坑一个接一个。我把碰到的几个典型问题按排查过程记录下来你以后遇到一模一样的情况可以直接照着处理。6.1 DHT11偶发读取失败中断是元凶现象程序刚烧录进去一切正常跑个几分钟后DHT11返回的数据突然变成了0OLED上显示温度0.0℃湿度0.0%。排查链路先怀疑DHT11硬件坏了换了新的模块问题依旧然后怀疑接线接触不良重新插拔杜邦线问题依旧最后用示波器抓数据线波形发现每次读取失败之前数据线上都出现了一个异常的脉冲。根因SysTick中断每毫秒触发一次如果DHT11读取过程中刚好被中断打断而且中断服务程序执行得稍微久了点就会错过数据位的高电平采样窗口导致读回来的字节变成0xFF或者0x00。校验和过不了数据被丢弃于是显示出默认值0。解决办法在DHT11_Read函数里进入读取阶段后关闭全局中断读完40位数据后再打开。因为整个读取过程也就几毫秒关中断对系统其他功能没有影响。6.2 MQ-135上电数值乱跳别急着调阈值现象刚上电时OLED上显示的空气质量ADC值从几百一路涨到两千多然后又慢慢掉下来看起来非常诡异。排查链路一开始以为是ADC参考电压不稳加了一堆滤波电容还是没解决后来查数据手册才发现MQ-135内部有个加热电阻传感器需要足够的预热时间才能达到稳定的工作温度。在预热阶段气体传感器的响应特性是完全不正常的。根因传感器还没有进入正常工作状态。解决办法代码里已经加了5分钟预热静默期预热期间只采集基准值不触发报警。这个时间不要省虽然等待有点煎熬但对报警准确性的提升非常明显。6.3 ADC采样值跳动VDDA滤波是关键现象室温稳定、空气清新但MQ-135的ADC采样值一直在±100的范围里跳。排查链路先怀疑是传感器本身的波动但把传感器断开后ADC引脚悬空采出来的值照样跳然后用万用表测3.3V电源发现波形上有高频纹波检查原理图发现VDDA直接跟VDD串在一起没有任何滤波电路。根因数字电路的工作电流在快速变化时会在电源网络上产生纹波这个纹波直接耦合进了ADC的参考电压。解决办法VDDA通过一个10Ω电阻和1μF电容组成的RC滤波后接3.3V并且在软件上配合滑动平均滤波。改完之后采样值的跳动范围从±100降到了±10以内。6.4 蜂鸣器声音很小三极管驱动的问题现象蜂鸣器能响但声音跟蚊子叫一样贴到耳朵边才能听到。排查链路一开始认为是有源蜂鸣器本身功率小换了个大功率蜂鸣器还是不行用万用表测蜂鸣器两端的电压发现只有1.8V左右。问题不在蜂鸣器而在驱动电路。根因直接把蜂鸣器接到了GPIO上。GPIO高电平时的输出电压虽然接近3.3V但带载能力有限蜂鸣器一拉电流电压就掉下来了。解决办法改成三极管驱动方案GPIO → 1kΩ电阻 → S8050基极蜂鸣器接在5V和集电极之间。改完后蜂鸣器两端电压接近5V声音明显洪亮了很多。6.5 OLED显示花屏I2C速率降下来就好现象OLED偶尔显示乱码有时候花屏之后过几秒自己又恢复了。排查链路用逻辑分析仪抓I2C总线数据发现频率达到了360kHz左右但SSD1306的数据手册上写的是I2C时钟最大400kHz虽然没超过上限但结合杜邦线的寄生电容时序就变得不稳定了。根因标准库的I2C初始化里设置的时钟速率过高加上杜邦线较长信号质量变差。解决办法把I2C时钟从400kHz降到100kHz。OLED是显示设备100kHz的刷新速率完全够用不要盲目追求高速。这些坑有一个共同特点它们都不是代码逻辑错误而是在硬件和软件交界处产生的问题。这也是为什么做一个完整的项目比刷十道编程题收获大得多——你才能真正理解系统如何在实际物理世界中运作。如果说还有什么经验值得分享那就是开源项目最费时间的往往不是写代码本身而是把“我能跑”变成“你也能跑”。我在整理这套资料时光是Proteus仿真工程和文档就花了跟写固件差不多的时间。但看到别人按照文档一步一步把环境监测系统跑起来基本上不用再来问我“为什么我编译报错”“为什么我读出来的温度是0”就觉得这些整理工作完全值得。
返回列表