
ESP32-S3驱动MQ-135空气质量传感器实战ADC采集与阈值判断最近在做一个小型环境监测项目需要检测空气中的有害气体浓度于是就用上了MQ-135这款传感器。很多刚开始接触ESP32-S3的朋友可能会觉得驱动一个模拟传感器听起来有点复杂既要配置ADC又要处理数据。其实只要理清思路一步步来你会发现这事儿并不难。今天我就以ESP32-S3开发板为例手把手带你完成MQ-135传感器的驱动。咱们不仅会读取它的模拟输出AO来获取精确的浓度值还会利用它自带的数字输出DO和比较器实现一个简单的阈值报警功能。整个过程我会结合代码详细讲解包括硬件怎么连、ADC怎么配置、代码怎么移植以及实际应用中需要注意的那些“坑”。1. 认识MQ-135传感器在动手接线写代码之前咱们先花几分钟了解一下MQ-135到底是干什么的这对后续的理解和调试很有帮助。MQ-135是一款用来检测空气质量的气体传感器。它的核心敏感材料是二氧化锡SnO2这种材料在清洁空气中导电率比较低。但是当传感器周围存在氨气、硫化物、苯系蒸汽、烟雾等污染气体时它的导电率会随着气体浓度的增加而升高。简单来说空气越“脏”传感器的导电性越好输出的模拟电压信号也就越高。它是一款成本低、适合多种应用场景的传感器。传感器模块上有4个引脚我们需要关注其中两个AO (Analog Output) 模拟输出直接输出一个与气体浓度相关的电压值0-VCC。我们需要用ESP32-S3的ADC模数转换器来读取这个电压从而计算出浓度百分比或估算浓度值。DO (Digital Output) 数字输出模块上通常还有一个蓝色的电位器和一个LM393电压比较器芯片。AO的电压会和这个电位器设定的阈值电压进行比较。当AO电压高于阈值即浓度超标时DO引脚会输出低电平0反之输出高电平1。这就提供了一个简单的“超标/正常”开关信号不需要写复杂的判断逻辑。注意模块上的电位器就是用来调节报警阈值的。顺时针旋转阈值电压升高需要更高的浓度才会触发报警逆时针旋转则更灵敏。2. 硬件连接与引脚分配接线是第一步一定要接对不然代码写得再好也没用。所需材料ESP32-S3开发板这里以ESP32S3R8N8为例MQ-135传感器模块杜邦线若干接线方案MQ-135模块的供电电压是3.3V-5V为了简单起见我们直接用ESP32-S3的3.3V给它供电。接线表如下MQ-135模块引脚连接到ESP32-S3引脚说明VCC3.3V电源正极GNDGND电源地AOGPIO1(或其他ADC引脚)模拟信号输出接ESP32的ADC输入通道DOGPIO2(或其他任意GPIO)数字信号输出接ESP32的普通输入GPIO提示ESP32-S3的ADC1通道对应的GPIO是有限的例如GPIO1, GPIO2, GPIO3, GPIO4等具体需查阅芯片手册。我这里选择GPIO1作为ADC输入对应ADC1通道0GPIO2作为数字输入你可以根据自己板子的实际情况调整。接好线后给传感器通电它中心的电热丝会开始预热通常需要几十秒到一分钟才能稳定工作这是正常现象。3. 软件驱动代码移植与解析原始资料提供了一个从DHT11例程修改而来的驱动代码。咱们不直接照搬而是理解其原理然后自己动手创建一个清晰的驱动。3.1 创建驱动文件首先在你的工程中新建两个文件bsp_mq135.c和bsp_mq135.h。bsp是“板级支持包”的缩写用来存放外设的驱动代码。3.2 编写头文件 (bsp_mq135.h)头文件主要用于宏定义和函数声明让代码更清晰、易于修改。#ifndef _BSP_MQ135_H_ #define _BSP_MQ135_H_ #include stdio.h #include driver/gpio.h #include freertos/FreeRTOS.h #include freertos/task.h #include driver/adc.h #include esp_adc_cal.h // 硬件引脚配置 (根据你的实际接线修改) #define MQ135_AO_PIN GPIO_NUM_1 // AO接在GPIO1上 #define MQ135_DO_PIN GPIO_NUM_2 // DO接在GPIO2上 // ADC参数配置 #define ADC_CHANNEL ADC_CHANNEL_0 // GPIO1 对应 ADC1 通道0 #define ADC_WIDTH ADC_WIDTH_BIT_12 // 12位分辨率 #define ADC_ATTEN ADC_ATTEN_DB_11 // 衰减11dB量程约0-3.3V #define ADC_UNIT ADC_UNIT_1 // 使用ADC1 #define DEFAULT_VREF 1100 // 用于校准的默认参考电压(mV) #define ADC_SAMPLES 30 // 采样次数用于求平均值滤波 // 函数声明 void mq135_init(void); // 初始化函数 uint32_t mq135_read_adc_raw(void); // 读取ADC原始值 uint32_t mq135_read_adc_voltage(void); // 读取计算后的电压值(mV) uint8_t mq135_read_digital(void); // 读取DO引脚数字状态 uint8_t mq135_get_percentage(void); // 获取浓度百分比简易版 #endif代码关键点解释引脚定义MQ135_AO_PIN和MQ135_DO_PIN必须和你实际的硬件接线一致。ADC配置ADC_WIDTH_BIT_12ESP32-S3的ADC分辨率设为12位读数值范围是0-4095。ADC_ATTEN_DB_11这是衰减设置。DB_11对应满量程约3.3V正好匹配我们的供电电压。如果传感器输出范围小可以选用DB_0(1.1V)等以获得更高精度。ADC_SAMPLESADC采样容易受到干扰我们采集30次然后取平均值可以有效地平滑数据减少跳动。3.3 编写源文件 (bsp_mq135.c)这是驱动的核心包含了初始化和数据读取的所有逻辑。#include bsp_mq135.h // 定义一个ADC校准特性结构体指针用于存储校准参数 static esp_adc_cal_characteristics_t *adc_chars NULL; // 简单的毫秒延时函数基于FreeRTOS static void delay_ms(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } /** * brief 初始化MQ-135传感器所需的硬件GPIO和ADC */ void mq135_init(void) { // 1. 配置DO引脚为输入模式 gpio_config_t io_conf { .pin_bit_mask (1ULL MQ135_DO_PIN), .mode GPIO_MODE_INPUT, .pull_up_en GPIO_PULLUP_DISABLE, // 模块内部可能有上拉这里先禁用 .pull_down_en GPIO_PULLDOWN_DISABLE, .intr_type GPIO_INTR_DISABLE }; gpio_config(io_conf); // 2. 配置ADC // 2.1 设置ADC位宽分辨率 adc1_config_width(ADC_WIDTH); // 2.2 配置指定通道的衰减 adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN); // 3. 进行ADC校准非常重要能显著提高测量精度 adc_chars calloc(1, sizeof(esp_adc_cal_characteristics_t)); // 参数ADC单元、衰减、位宽、默认参考电压、存储校准结果的结构体 esp_adc_cal_characterize(ADC_UNIT, ADC_ATTEN, ADC_WIDTH, DEFAULT_VREF, adc_chars); printf(MQ135 Init Done.\n); } /** * brief 读取ADC原始值0-4095 * return ADC原始值 */ uint32_t mq135_read_adc_raw(void) { uint32_t adc_sum 0; // 采样多次求平均值以抑制噪声 for (int i 0; i ADC_SAMPLES; i) { adc_sum adc1_get_raw(ADC_CHANNEL); } return (adc_sum / ADC_SAMPLES); } /** * brief 读取并计算实际的电压值单位毫伏 mV * return 电压值 (mV) */ uint32_t mq135_read_adc_voltage(void) { uint32_t raw mq135_read_adc_raw(); // 使用校准参数将原始值转换为电压值 uint32_t voltage esp_adc_cal_raw_to_voltage(raw, adc_chars); return voltage; // 例如返回 1500 表示 1.5V } /** * brief 读取DO引脚的数字状态 * return 0: 浓度超过阈值DO输出低电平 1: 浓度正常DO输出高电平 * note 具体电平逻辑取决于你的模块常见是低电平报警。 */ uint8_t mq135_read_digital(void) { // 读取GPIO电平DO引脚为低电平时表示检测到气体超标 if (gpio_get_level(MQ135_DO_PIN) 0) { return 0; // 报警状态 } else { return 1; // 正常状态 } } /** * brief 获取一个简单的浓度百分比0-100% * return 浓度百分比基于ADC读数满量程的估算 * note 这是一个非常粗略的估算实际浓度需要根据传感器数据手册进行标定和换算。 */ uint8_t mq135_get_percentage(void) { uint32_t raw mq135_read_adc_raw(); // 将12位ADC原始值映射到0-100% uint8_t percentage (raw * 100) / 4095; return percentage; }代码关键点与踩坑提醒ADC校准 (esp_adc_cal_characterize)这是提升ESP32 ADC测量精度的关键一步ESP32系列的ADC有非线性误差通过校准可以很大程度上修正它。不校准的话测出来的电压可能和万用表量的相差几十甚至上百毫伏。DEFAULT_VREF是内部参考电压的典型值校准过程会计算出一个更准确的实际值。数字输出逻辑mq135_read_digital()函数中我假设DO引脚输出低电平0表示报警。但有些模块设计可能相反。如果你发现逻辑不对可以调整这个判断条件或者用螺丝刀调节模块上的蓝色电位器看看电平变化是否符合预期。百分比计算mq135_get_percentage()函数计算的是“ADC读数占满量程的百分比”并不是真实的气体浓度PPM值MQ-135的电阻-浓度关系是非线性的且受温湿度影响。要获得准确的PPM你需要查阅传感器的数据手册找到其灵敏度特性曲线并进行复杂的换算和温湿度补偿。对于一般定性判断比如“空气变差了”这个百分比也足够直观。4. 在主程序中调用与测试驱动写好了最后就是在主函数里调用它并把数据打印出来看看。#include stdio.h #include bsp_mq135.h void app_main(void) { // 1. 初始化传感器 mq135_init(); printf(MQ-135 Sensor Test Start...\n); while (1) { // 2. 读取模拟值并计算电压 uint32_t adc_raw mq135_read_adc_raw(); uint32_t voltage_mv mq135_read_adc_voltage(); uint8_t percentage mq135_get_percentage(); // 3. 读取数字输出状态 uint8_t digital_state mq135_read_digital(); // 4. 打印结果 printf(ADC Raw: %ld, Voltage: %ld mV, Percentage: %d%%, adc_raw, voltage_mv, percentage); if (digital_state 0) { printf( | [ALARM!] Gas concentration exceeds threshold!\n); } else { printf( | [OK] Gas concentration is normal.\n); } // 5. 延时1秒再读 delay_ms(1000); } }将代码编译、烧录到ESP32-S3开发板后打开串口监视器波特率通常为115200你就能看到每秒一行的输出数据。如何测试正常空气观察此时的ADC原始值、电压和百分比。这是你的“基线”。触发报警向MQ-135传感器附近呼一口气含有二氧化碳或者用打火机不点火释放一些丁烷气体。你应该会看到ADC值和百分比迅速上升同时DO状态很可能变为[ALARM!]。调节阈值尝试用螺丝刀旋转模块上的蓝色电位器你会发现在气体浓度不变的情况下拧动电位器可以改变DO的输出状态。这就是在调节硬件比较器的触发点。5. 项目进阶与思考基本的驱动已经完成了但要想把它用到实际项目中还有几点可以深入数据标定如前所述ADC百分比不是真实浓度。你需要用已知浓度的标准气体或参考其他专业仪器来标定你的传感器建立ADC读数与实际PPM值的对应关系公式。这是一个专业且必要的过程。温湿度补偿MQ系列传感器对温湿度很敏感。最好能搭配一个温湿度传感器如DHT22根据数据手册提供的修正曲线来补偿读数提高准确性。软件阈值判断除了使用硬件DO你完全可以在软件里做更灵活的阈值判断。比如在app_main的循环里判断percentage是否超过某个值例如60%然后通过另一个GPIO控制LED闪烁或蜂鸣器报警。数据上传结合ESP32-S3的Wi-Fi功能你可以很容易地将浓度数据和报警状态上传到云平台如阿里云、ThingsBoard等实现远程环境监控。驱动MQ-135的核心就在于理解模拟和数字两种输出方式并正确配置ESP32-S3的ADC。希望这篇教程能帮你顺利打通这个环节。在实际连接时如果数据跳动很大除了软件滤波也要检查一下电源是否稳定接线是否牢固。