树莓派使用Golang+MQ135检测室内空气质量
阅读原文时间:2023年07月21日阅读:1

MQ135是一个比较便宜的空气质量传感器,可以用在家庭以及工业场所中。树莓派是一个小巧但很强大的卡片电脑,基于Linux,同时提供了很多硬件接口,方便开发出各种电子产品。Golang是一款简单高效的语言,编译结果为native代码,无需提前安装运行环境和虚拟机。

关于MQ135需要提前说明几点:

1、自身无法确定污染气体种类。最开始想用MQ135获取空气中各种有害气体的浓度,比如甲苯、CO、酒精等,搞了几天发现这个行不通,虽然可以判断空气质量好坏,但是并不能确定是何种气体导致的空气污染。因为MQ135在多种有害气体浓度升高的时候都会增加气敏材料的导电率,并不能区分出是哪种气体导致的。但是如果在某些特殊的环境中,污染物只可能是某种气体时,倒是可以准确的检测。

2、气体浓度检测范围是10-1000ppm。国际标准将室内苯浓度限定为0.087mg/m3,换算成ppm就更小了,不知道在小于10ppm的环境中实际表现如何。对于想检测室内苯系物是不是超标,这个就很难判断,除非超过了10ppm,那就是很严重的污染了。

3、使用前需要先校准。MQ135的数据表中给出了各种气体不同浓度对应的传感器电阻相对Ro的比值,Ro是氨气在100ppm、20°C 、 65%相对湿度下的传感器电阻值,但是说明书中没有给出这个值是多少,不过可以根据指定的气体浓度值及对应的电阻值进行计算,这就得有个标准的实验环境。同时根据说明书预热时间需要48小时,所以使用这个传感器应该是一直开着,偶尔可以短暂关机或重启。

4、测量精度问题。这里说的是带PCB板的MQ135,板子上自带一个负载电阻,但是阻值只有1KΩ或者2KΩ,所以测出的电压会比较小,精度可能不够,对于CO2这种空气中浓度比较高的气体,值会在比较大的范围跳动。这个带板子的MQ135的说明书实际没有明确说明可以检测CO2,但是说明书的图表中有一条曲线实际就是CO2的,而且有些资料中提到是可以检测的,只不过要把负载电阻搞的很大精度才够。


下边就开始介绍实验步骤了。

  • 树莓派3B+(需要能提供5V电压;已安装好操作系统,能ssh连接)
  • MQ135(PCB板自带负载电阻,输出电压的模拟信号)
  • MCP3008(将MCP3008的模拟信号转换为数字信号)
  • DHT11(温湿度传感器,MQ135会受到温度和湿度的影响)
  • 面包板(安置DHT11、MCP3008、DHT11)
  • 杜邦线若干(母对母头、公对公头、公对母头都需要)

有些文章中还使用了电平转换器件,将MQ135产生的5V电平信号转换为3.3V电平信号,可能是因为他们的树莓派不能读取5V信号,这个在3B+上经过实测5V信号也没有问题,所以我认为不是必要的,当然加上它也没问题,计算的时候输入电压按照3.3V就可以了。这里不使用它的另一个原因,安装在面包板上时排针与它经常接触不良,可能需要焊接下,比较麻烦,索性就不用了。

这里仅表示连线关系,实际走线可以酌情处理,比如Ground有很多阵脚可以提供,5V针脚也有两个。

SPI是串行外设接口,因为读取MCP3008的数据需要使用SPI协议,但是树莓派默认并没有开启它,所以需要手动开启SPI。执行命令:

sudo raspi-config

然后选择P4 SPI。

重启树莓派,以使SPI生效。

这里专门编写了一个模块,以方便读取MQ135和DHT11的数据,所以先安装这个模块。

go get github.com/bosima/go-pidriver

然后导入这个模块。

import "https://github.com/bosima/go-pidriver"

因为涉及的内容比较多,这里分步介绍:

(1)初始化树莓派针脚映射。

drivers.OpenRPi()

(2)初始化DHT11,以及采集当前温度的方法。

    dht11, err := drivers.NewDHT11(4)
    if err != nil {
        fmt.Println(err)
        return
    }

func readTR(dht11 *drivers.DHT11) (float64, float64, error) {
    rh, tmp, err := dht11.ReadData()

    if err != nil {
        return 0.0, 0.0, err
    }

    return rh, tmp, nil
}

(3)初始化MCP3008。最后1个参数代表参考电压为5V。MCP3008实际有三个电压的参数:工作电压、参考电压、模拟信号电压,参考电压和模拟信号电压都不能超过工作电压,参考电压是用于模拟信号转换为数字信号的比较值,所以一般参考电压也不应该低于模拟信号电压,工作电压和参考电压一般使用相同的值,这里都使用的5V。

    mcp, err := drivers.NewMCP3008(0, 0, 5.0)
    if err != nil {
        fmt.Println(err)
        return
    }

(4)初始化MQ135。

mq135 := drivers.NewMQ135(mcp, 5.0, "CO2", 10, 2.0)
  • vcc是工作电压,这里统一了加热电压和回路电压,按照说明书加热电压为5.0V±0.2V,回路电压为≤24V,这里统一为5V。
  • gas要测量的气体,支持测量二氧化碳、一氧化碳、氨气、酒精、甲苯、丙酮。因为要计算基准参考电阻,必须有一个实验环境,因为大气中二氧化碳的浓度比较容易获取,所以这里用了二氧化碳。
  • ro是基准参考电阻值,表示在含100ppm氨气、20℃/65%RH下的传感器电阻值,这里因为没有标准实验环境,所以先随便写一个10,后边校准的步骤中会计算这个值。
  • rl是负载电阻的阻值。这个要看PCB上焊接的电阻,我这里上边写的是202,代表2KΩ,有的可能写的102,代表1KΩ。下图红框中的电阻即为负载电阻。

(5)校准Ro。每隔2秒钟采集下温湿度,然后再传入CO2的浓度,大气中CO2的浓度可以使用这个常量值,但是由于碳排放问题,基本每年都在增高的,可以通过这个网站获取:https://www.co2.earth/。因为MQ135需要较长时间的预热,所以校准前需要提前预热好,校准时保持稳定的气体环境,校准次数可以根据实际情况调整。

    ro := 0.0
    sampleTimes := 60
    for i := 0; i < sampleTimes; i++ {
        // use realtime temperature and humidity
        for {
            rh, tmp, err = readTR(dht11)
            if err == nil {
                break
            }
            time.Sleep(time.Second * 2)
        }

        // Gas concentration in natural environment:
        // CO2 414ppm
        // unit transfer: {y}ppm = 24.5 * {x}mg/m^3 /{M}
        ro += mq135.MeasureRo(tmp, rh, 414.0)
        time.Sleep(time.Second * 2)
    }
    mq135.Ro = ro / float64(sampleTimes)
    fmt.Printf("ro: %f \n", mq135.Ro)

(6)测试气体浓度。这里写了一个死循环,每隔2秒钟测量1次二氧化碳浓度。

for {
        rh, tmp, err := readTR(dht11)
        if err != nil {
            time.Sleep(time.Second * 2)
            continue
        }

        concentration, err := mq135.MeasureGasConcentration(tmp, rh)
        if err != nil {
            fmt.Println(err)
            continue
        }

        fmt.Printf("\rtmp: %.2f, rh: %.2f, concentration: %f", tmp, rh, concentration)
        os.Stdout.Sync()

        time.Sleep(time.Millisecond * 2000)
    }

看一下实际输出结果:

面对MQ135呼吸,会看到CO2气体浓度值快速上升,从MQ135挪开一会,会看到浓度值又降回原来的水平。

(1)传感器电阻

MQ135输出的是电压信号,那么根据电压信号怎么得到传感器的电阻值。下边是MQ135的简化电路图,VC是输入电压,RS是传感器实际电阻,VRL是输出电压,RL是负载电阻,问题可以这样描述:已知VC、VRL和RL,求RS的值。

根据欧姆定律,可以得出:RS=(VC/VRL-1)*RL

另外需要考虑温湿度对电阻值的影响,下图是100ppm氨气在不同温湿度条件下的Rs/Ro值的曲线。从这个图表中的数据可以拟合温湿度的多项式:z=p00 + p10*x + p01*y + p20*x^2 + p11*x*y,x代表温度,y代表湿度,p00、p10、p01、p20、p11都是一些常数,具体值请查看Github源代码。

因此最终计算RS时还需要做一下校正:RS=RS/z

(2)模拟信号转数字信号

MCP3008采用逐次逼近的算法。首先会取工作电压的1/2,这里就是2.5V,然后通过内置的DAC转换为模拟电压与输入信号进行比较,如果2.5V大于输入电压则最高位为0,否则为1;然后取0*2.5+5*1/4与输入电压进行比较,如果大于则次高位为0,否则为1;然后再取0*2.5+5*1/4+ 5*1/8与输入信号进行比较,如果大于则第三位为0,否则为1;如此依次比较取得最终10位数值,即0-1023之间的数字。

由逐次逼近算法可知,电压被分成了1024份,所以得到的数字信号转化为电压时:V={DAC}*(5/1024)

(3)MQ135 Ro计算

根据MQ135的数据图表可以拟合气体浓度与电阻比值之间的公式为:Rs/Ro=ax^b,这是一个幂函数,对于某种特定的气体,其中a、b为常数,x为气体浓度,Rs前边已经有方法可以计算出来,因此只要得到x就可以得到Ro的值。

R0 = RS * (x/ A)-1/B ,其中A=(1 / a)1/b ,B=1/b。

这里因为没有标准的实验环境,所以取个巧,使用大气中的二氧化碳浓度标准值作为清洁空气中的二氧化碳浓度值,这个虽然和当前测试环境不一定匹配,但是只能凑合用用了。另外因为这里用的MQ135板载负载电阻只有2千欧,所以对CO2实际的测量精度也会有比较大的影响。

(4)气体浓度计算

有了Ro,反过来就可以根据Rs计算气体浓度了:c = A * (RS / R0)B,A=(1 / a)1/b ,B=1/b,a、b气体浓度幂函数之中的常量。


因之前对硬件和传感器的知识了解甚少,仓促之间可能有错误之处,还望指正。

此文所有源代码已经开源到GitHub,欢迎取用:https://github.com/bosima/go-pidriver

此文参考了大量网络资料,特别是这篇文章:https://pslab.io/blog/measuring-co2-with-mq135/

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章