ESP32-S3驱动MQ-3酒精传感器实战:ADC采集与阈值判断代码详解

ESP32-S3驱动MQ-3酒精传感器实战:ADC采集与阈值判断代码详解 ESP32-S3驱动MQ-3酒精传感器实战ADC采集与阈值判断代码详解最近在做一个智能家居安全项目需要检测室内酒精浓度防止燃气泄漏或酒精挥发引发危险。我选择了常见的MQ-3酒精传感器搭配ESP32-S3开发板来实现。很多刚开始接触ESP32和模拟传感器的朋友可能会觉得ADC采集和阈值判断有点复杂其实一步步来并不难。今天我就把自己在实际项目中的驱动代码和配置经验分享给大家手把手教你如何让ESP32-S3和MQ-3传感器“对话”。1. 认识MQ-3酒精传感器MQ-3是一款半导体气体传感器它对酒精蒸汽特别敏感。简单来说它的核心是一个用二氧化锡(SnO₂)材料做成的“嗅探器”。在清洁空气中这个材料的导电性很差电阻很大。但当空气中出现酒精蒸汽时酒精分子会吸附在材料表面导致它的导电性变好电阻变小。传感器内部电路就是通过测量这个电阻的变化来感知酒精浓度的。MQ-3模块通常有4个引脚提供了两种输出方式非常方便引脚名称类型功能说明VCC电源工作电压范围3.3V-5VESP32-S3的3.3V输出可以直接供电GND电源接地AO模拟输出输出一个与酒精浓度相关的模拟电压值需要连接ESP32的ADC引脚进行读取DO数字输出模块内部通过一个比较器通常是LM393将模拟信号与一个可调阈值比较输出高低电平。当浓度超过设定阈值时DO引脚输出低电平或高电平取决于模块设计注意不同厂家生产的MQ-3模块其DO引脚的电平逻辑可能不同有的高电平报警有的低电平报警。咱们教程里用的代码是基于DO引脚在检测到高浓度酒精时输出低电平0来写的。拿到模块后最好先用万用表测一下或者看模块说明书确认。AO模拟输出这个引脚输出的电压是连续变化的浓度越高电压通常也越高具体关系要看传感器特性曲线。我们需要用ESP32-S3的ADC模数转换器把这个电压值“翻译”成单片机可以理解的数字量从而得到更精确的浓度信息。DO数字输出这个引脚相当于一个“开关”。模块上通常有一个蓝色的电位器你可以用螺丝刀旋转它来设定报警阈值。当酒精浓度超过你设定的值时DO引脚的电平就会翻转。这种方式使用起来非常简单接个GPIO读一下电平就行适合做简单的超标报警。2. 硬件连接与工程准备2.1 连线图把ESP32-S3开发板和MQ-3模块连接起来非常简单只需要四根线ESP32-S3 3.3V-MQ-3 VCCESP32-S3 GND-MQ-3 GNDESP32-S3 GPIO1-MQ-3 AO(用于ADC采集模拟值)ESP32-S3 GPIO2-MQ-3 DO(用于读取数字报警信号)这里我选择GPIO1和GPIO2只是举例你可以根据自己板子的可用引脚情况在代码里修改成其他支持ADC和普通GPIO输入的引脚。2.2 创建驱动文件在ESP-IDF工程中好的习惯是为每个外设模块创建独立的驱动文件。这样代码结构清晰也方便以后移植到其他项目。在你的项目components目录下如果没有就新建一个创建一个名为mq3的文件夹。在mq3文件夹内新建两个文件bsp_mq3.c和bsp_mq3.h。bsp是“板级支持包”的缩写这种命名方式在嵌入式开发中很常见。接下来咱们就把核心的驱动代码写在这两个文件里。3. 头文件定义与配置 (bsp_mq3.h)头文件的主要作用是声明用到的函数、定义引脚和参数这样在主程序里只要#include bsp_mq3.h就能使用所有功能了。#ifndef _BSP_MQ3_H_ #define _BSP_MQ3_H_ #include stdio.h #include inttypes.h #include sdkconfig.h #include driver/gpio.h #include freertos/FreeRTOS.h #include freertos/task.h #include esp_rom_sys.h #include esp_timer.h #include driver/uart.h #include rom/ets_sys.h #include esp_system.h #include driver/gptimer.h #include esp_log.h #include freertos/queue.h #include driver/spi_master.h #include nvs_flash.h #include esp_adc/adc_cali_scheme.h #include esp_adc/adc_cali.h #include driver/adc.h #include esp_adc_cal.h /* 引脚定义 - 根据你的实际接线修改 */ #define MQ3_AO_PIN 1 // 连接MQ-3模块的AO引脚 #define MQ3_DO_PIN 2 // 连接MQ-3模块的DO引脚 /* ADC校准相关参数 */ #define DEFAULT_VREF 1100 // 默认参考电压单位mV。ESP32-S3内部参考电压一般为1100mV /* ADC通道与参数配置 */ #define channel ADC_CHANNEL_0 // 使用ADC1的通道0对应GPIO1 (如果AO接在GPIO1) #define width ADC_WIDTH_BIT_12 // ADC分辨率设为12位 (0-4095) #define atten ADC_ATTEN_DB_11 // 输入衰减设为11dB量程约为0-3.3V #define unit ADC_UNIT_1 // 使用ADC1 /* 采样次数多次采样取平均可以减小噪声干扰 */ #define SAMPLES 30 /* 函数声明 */ void ADC_MQ3_Init(void); unsigned int Get_Adc_MQ3_Value(char CHx); unsigned int Get_MQ3_Percentage_value(void); char Get_MQ3_DO_value(void); /* 延时函数声明 (用于传感器稳定或轮询间隔) */ void delay_us(unsigned int us); void delay_ms(unsigned int ms); void delay_1us(unsigned int us); void delay_1ms(unsigned int ms); #endif关键配置解释ADC_ATTEN_DB_11这是ADC的衰减设置。ESP32的ADC输入电压范围是可调的。DB_11表示最大衰减允许测量的电压范围大约是0-3.3V正好匹配我们3.3V供电的传感器输出。如果你用5V供电的模块AO输出可能超过3.3V就需要在AO引脚和ESP32之间加分压电阻否则可能烧坏ADCADC_WIDTH_BIT_12分辨率设为12位。这意味着ADC会把0-3.3V的电压分成2^124096个等级数字量范围是0-4095。数字量越大表示电压越高。SAMPLES 30ADC采样容易受到电源噪声、电磁干扰等影响单次读数可能跳动。采集30次然后取平均值可以得到更稳定、可靠的结果。4. 驱动函数实现 (bsp_mq3.c)这里是具体的代码实现部分咱们一个一个函数来看。4.1 初始化函数ADC_MQ3_Init这个函数负责初始化两个部分一是配置DO引脚为输入模式二是配置和校准ADC。#include bsp_mq3.h // 用于存储ADC校准特性的指针校准能提高ADC测量精度 esp_adc_cal_characteristics_t *adc_chars; // 几个简单的延时函数基于FreeRTOS和ESP32内置延时实现 void delay_ms(unsigned int ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } void delay_us(unsigned int us) { ets_delay_us(us); } void delay_1ms(unsigned int ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } void delay_1us(unsigned int us) { ets_delay_us(us); } /****************************************************************** * 函数名称ADC_MQ3_Init * 函数说明初始化MQ-3传感器所需的硬件GPIO和ADC * 形 参无 * 返 回 值无 ******************************************************************/ void ADC_MQ3_Init(void) { // 1. 配置DO引脚为输入模式 gpio_config_t do_pin_config { .pin_bit_mask (1ULL MQ3_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(do_pin_config); // 应用配置 // 2. 配置ADC adc1_config_width(width); // 设置ADC1的分辨率为12位 // 注意原始代码中有一行被注释掉的通道配置我们需要根据实际使用的引脚来配置 // 假设AO接在GPIO1 (ADC1_CHANNEL_0) adc1_config_channel_atten(channel, atten); // 配置指定通道的衰减 // 3. 为ADC校准分配内存并初始化校准参数 adc_chars calloc(1, sizeof(esp_adc_cal_characteristics_t)); if (adc_chars NULL) { printf(错误无法为ADC校准特性分配内存\n); return; } // 对ADC进行特性化为后续的原始值转电压值做准备 esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_chars); }提示esp_adc_cal_characterize函数非常重要。由于制造工艺差异每颗ESP32芯片的ADC都有微小的误差增益误差、偏移误差。这个函数会计算出一组校准系数后面用esp_adc_cal_raw_to_voltage函数就能把ADC原始值转换成更精确的电压值(mV)。不过咱们的示例代码里为了简化直接用了原始值比例计算实际要求高精度的项目建议使用电压转换。4.2 读取ADC原始值Get_Adc_MQ3_Value这个函数负责从指定的ADC通道读取模拟值并进行了软件滤波多次采样取平均。/****************************************************************** * 函数名称Get_Adc_MQ3_Value * 函数说明获取指定ADC通道的采样平均值软件滤波 * 形 参CHx - ADC通道号虽然这里固定用channel但函数保留了通道参数 * 返 回 值ADC采样平均值 (0-4095) ******************************************************************/ unsigned int Get_Adc_MQ3_Value(char CHx) { unsigned char i 0; unsigned int AdcValue 0; // 循环采样SAMPLES次例如30次 for(i0; i SAMPLES; i) { // 累加每次采样的原始值 AdcValue adc1_get_raw(CHx); // 可以加一个很小的延时避免采样过快 // ets_delay_us(10); } // 计算平均值 AdcValue AdcValue / SAMPLES; return AdcValue; }4.3 计算浓度百分比Get_MQ3_Percentage_value这个函数把ADC原始值转换成一个0-100%的百分比。注意这个百分比不是酒精浓度的体积百分比而是ADC量程的百分比。它表示当前传感器输出电压占满量程电压3.3V的比例。酒精浓度和这个百分比值的关系需要你根据传感器手册的曲线或者自己标定来确定。/****************************************************************** * 函数名称Get_MQ3_Percentage_value * 函数说明读取MQ-3的AO值并转换为百分比形式 * 形 参无 * 返 回 值ADC值占满量程的百分比 (0-100) ******************************************************************/ unsigned int Get_MQ3_Percentage_value(void) { int adc_max 4095; // 12位ADC的最大值 int adc_new 0; int Percentage_value 0; // 1. 获取滤波后的ADC原始值 adc_new Get_Adc_MQ3_Value(channel); // 2. 计算百分比(当前值 / 最大值) * 100 // 注意这里用了强制类型转换(float)避免整数除法丢失小数部分 Percentage_value ((float)adc_new / adc_max) * 100; return Percentage_value; }4.4 读取数字输出状态Get_MQ3_DO_value这个函数最简单就是读取连接DO引脚的GPIO电平根据电平判断是否超过阈值。/****************************************************************** * 函数名称Get_MQ3_DO_value * 函数说明获取MQ-3 DO引脚的电平状态阈值报警 * 形 参无 * 返 回 值0检测到高浓度酒精 (DO输出低电平)1未检测到 (DO输出高电平) * 备 注阈值可通过模块上的电位器调节 ******************************************************************/ char Get_MQ3_DO_value(void) { // 读取GPIO2的电平 if( gpio_get_level(MQ3_DO_PIN) 0 ) { return 0; // DO引脚为低电平表示浓度超过阈值 } else { return 1; // DO引脚为高电平表示浓度未超阈值 } }注意这里返回值的逻辑是基于我的模块设定的低电平报警。如果你的模块逻辑相反高电平报警只需要把if判断里的0和1对调一下或者修改返回值含义的注释即可。最稳妥的方法是给传感器喷一点酒精或靠近酒瓶口观察DO引脚电平变化从而确定你的模块逻辑。5. 在主程序中调用驱动写好了最后就是在ESP32-S3的app_main函数里初始化并循环读取数据了。#include stdio.h #include bsp_mq3.h void app_main(void) { // 1. 初始化MQ-3传感器配置GPIO和ADC ADC_MQ3_Init(); printf(MQ-3酒精传感器演示程序启动\r\n); // 2. 主循环每隔1秒读取一次数据 while(1) { // 读取并打印模拟量百分比 unsigned int perc Get_MQ3_Percentage_value(); printf(酒精传感器AO值百分比: %d%%\r\n, perc); // 读取并打印数字量报警状态 char do_state Get_MQ3_DO_value(); if(do_state 0) { printf(状态: 警告酒精浓度超标\r\n); } else { printf(状态: 正常\r\n); } printf(------------------\r\n); // 延时1秒 delay_1ms(1000); } }把代码编译、烧录到ESP32-S3开发板打开串口监视器波特率115200你就能看到实时的传感器数据输出了。对着传感器哈气含微量酒精或者靠近酒精源观察百分比数值和报警状态的变化。几个实战经验与坑点传感器预热MQ-3这类半导体传感器通电后需要一段时间几分钟才能稳定输出。刚上电的读数是不准的最好在程序初始化后延时一段时间再开始正式测量。ADC噪声ESP32的ADC在有些开发板上噪声可能比较大。除了代码里做的软件滤波硬件上可以尝试在AO引脚对地加一个0.1uF的滤波电容电源走线尽量粗且稳定。浓度标定Get_MQ3_Percentage_value函数返回的百分比不是ppm百万分之一浓度值。要得到真实的酒精浓度你需要查阅MQ-3的数据手册找到其电阻比Rs/R0与气体浓度的关系曲线图然后通过公式进行换算。或者用一个已知浓度的标准气体进行标定。DO阈值调节模块上的蓝色电位器就是用来调节数字输出阈值的。顺时针旋转通常提高阈值更不容易报警逆时针旋转降低阈值更敏感。调节时结合串口打印的AO百分比值找到一个合适的报警点。