项目场景:

笔者前段时间接触到了一个环境监测类的项目,需要对空气质量进行读取。也因此买了部分气体类的传感器进行调试。调试过程中就遇到了这么一个粉尘传感器——GP2Y1010AU0F。在树莓派上很多资料对应这个模块记录是少之又少的。接下来笔者就记录一下这个模块在树莓派上的使用与心得,希望能给读者一定的帮助。


例程缺失:

笔者的传感器是在淘宝的一家微雪电子购买的灰尘传感器 GP2Y1014AU0F PM2.5 粉尘颗粒 雾霾 检测仪。本来看说明看他是ADC模拟信号传输的,想着应该是蛮简单的。结果却是踩了个大坑。
在这里插入图片描述
微雪电子提供的模块是底下有含PCB板的,这相对其他店需要手动焊接还是比较方便的。他们也给了模块的相应资料与代码例程,里面也较为详细的解释了该模块的工作原理。可惜的是给只有STM32与Arduino的代码例程。

原因分析:

看到给的例程里没有树莓派的,笔者也是有点懵的,毕竟笔者的树莓派都是在这家店买的。有可能是因为树莓派缺少AD转换模块,写起来比较复杂吧。
不过笔者有买过一个传感器集成模块Sense HAT (B),里面有对AD的转化。也曾为其写过一篇博客多传感器(大气压 温湿度 气体浓度ADC采样)集合。当时笔者也做的是这个项目,只不过当时粉尘数据一直出不来,就没写进去。


问题描述:

既然没有例程,那就只能自己写。刚开始笔者没有注意到还有灯的驱动这么个条件。只是仿照着常规ADC的写,结果可想而知(采样一直处于0的位置)。。。
看到如下图这么个控制原理,我才发现这个不是想象中那样的简单。
在这里插入图片描述
这个模块是需要开启内置的一个LED灯,等待0.28ms才能稳定读取数值。STM32与Arduino的代码中1ms都是delay(1000)的。但是树莓派time.sleep(1)就是1秒。因此0.28ms相当于time.sleep(0.00028)。
接下来是接线部分:
在这里插入图片描述
在这里插入图片描述
灯是用杜邦线接到了GPIO.4处,对应的物理引脚编码为16
在这里插入图片描述

根据资料中的Arduino代码,我写出了如下代码(附上ADC代码源)。
main.py

# coding=UTF-8
import RPi.GPIO as GPIO
from ADC import ADS1015
from ADC import ADS_POINTER_CONFIG
import time
import math
import smbus
#import serial

COV_RATIO = 0.2 # //ug/mmm / mv
NO_DUST_VOLTAGE = 400 # //mv
SYS_VOLTAGE = 5000  

density=0.0
voltage=0.0
#int adcvalue=0
def SendVideo():
    ads1015=ADS1015()
    state=ads1015._read_u16(ADS_POINTER_CONFIG) & 0x8000 #    气体传感器连接确立
    
    if(state!=0x8000):
        print("\nADS1015 Error\n")
        # 收集气体数据
        
    GPIO.setmode(GPIO.BOARD)
    IN1 = 16
    GPIO.setwarnings(False)
    GPIO.setup(IN1,GPIO.OUT)    # 初始化二极管灯
    GPIO.output(IN1,GPIO.LOW) # 关闭二级管灯
    #ser = serial.Serial("/dev/ttyAMA0",9600)
    
    #def Filter(m):
        
    
    while 1:
        GPIO.output(IN1,GPIO.HIGH) # 启动二极管灯
        #AIN2_DATA=ads1015.ADS1015_SINGLE_READ(2)
        time.sleep(0.00028) # 等待0.28ms
        
        AIN2_DATA=ads1015.ADS1015_SINGLE_READ(2)
        #AIN0_DATA=((AIN0_DATA*2-64)/2000.00+0.02)*2
        
        time.sleep(0.00004) # 持续采集0.04ms
        GPIO.output(IN1,GPIO.LOW) # 关闭二级管灯
        time.sleep(0.00986)
        voltage=(5000/1024.0)*AIN2_DATA*1.1*2 # 计算气体浓度
        
        if(voltage >= NO_DUST_VOLTAGE):
            voltage -= NO_DUST_VOLTAGE
            density = voltage * COV_RATIO
        else:
            density = 0
    
        FG="The current dust concentration is:"+str(round(density,2))+" ug/m3"
        #FG=voltage
        print(FG)
        time.sleep(0.100)
        # F6=FG.encode('utf-8')

        # sock.send(str.encode(str(len(F6)).ljust(16)));
        # sock.send(F6)
                
if __name__ == '__main__':
    SendVideo()

ADC.py

#!/usr/bin/python
# -*- coding:utf-8 -*-
import time
import smbus
#i2c address
ADS_I2C_ADDRESS                   = 0x48

#Pointer Register
ADS_POINTER_CONVERT               = 0x00 # 指针_转换
ADS_POINTER_CONFIG                = 0x01 # 指针_配置
ADS_POINTER_LOWTHRESH             = 0x02 # 低阈值
ADS_POINTER_HIGHTHRESH            = 0x03 # 高阈值

#Config Register
ADS_CONFIG_OS_BUSY                  = 0x0000      #Device is currently performing a conversion 设备当前正在执行转换
ADS_CONFIG_OS_NOBUSY                = 0x8000      #Device is not currently performing a conversion 设备当前没有执行转换             
ADS_CONFIG_OS_SINGLE_CONVERT        = 0x8000      #Start a single conversion (when in power-down state) 开始单次转换(在掉电状态下) 
ADS_CONFIG_OS_NO_EFFECT             = 0x0000      #No effect 没有效果
ADS_CONFIG_MUX_MUL_0_1              = 0x0000      #Input multiplexer,AINP = AIN0 and AINN = AIN1(default 系统默认值) 输入复用器
ADS_CONFIG_MUX_MUL_0_3              = 0x1000      #Input multiplexer,AINP = AIN0 and AINN = AIN3 输入复用器
ADS_CONFIG_MUX_MUL_1_3              = 0x2000      #Input multiplexer,AINP = AIN1 and AINN = AIN3 输入复用器
ADS_CONFIG_MUX_MUL_2_3              = 0x3000      #Input multiplexer,AINP = AIN2 and AINN = AIN3 输入复用器
ADS_CONFIG_MUX_SINGLE_0             = 0x4000      #SINGLE,AIN0
ADS_CONFIG_MUX_SINGLE_1             = 0x5000      #SINGLE,AIN1
ADS_CONFIG_MUX_SINGLE_2             = 0x6000      #SINGLE,AIN2
ADS_CONFIG_MUX_SINGLE_3             = 0x7000      #SINGLE,AIN3
ADS_CONFIG_PGA_6144                 = 0x0000      #Gain= +/- 6.144V
ADS_CONFIG_PGA_4096                 = 0x0200      #Gain= +/- 4.096V
ADS_CONFIG_PGA_2048                 = 0x0400      #Gain= +/- 2.048V(default) 偏差正负2
ADS_CONFIG_PGA_1024                 = 0x0600      #Gain= +/- 1.024V
ADS_CONFIG_PGA_512                  = 0x0800      #Gain= +/- 0.512V
ADS_CONFIG_PGA_256                  = 0x0A00      #Gain= +/- 0.256V
ADS_CONFIG_MODE_CONTINUOUS          = 0x0000      #Device operating mode:Continuous-conversion mode 设备运行模式:连续转换模式        
ADS_CONFIG_MODE_NOCONTINUOUS        = 0x0100      #Device operating mode:Single-shot mode or power-down state (default) 设备运行模式:单发模式或掉电状态(默认)
ADS_CONFIG_DR_RATE_128              = 0x0000      #Data rate=128SPS 数据率
ADS_CONFIG_DR_RATE_250              = 0x0020      #Data rate=250SPS
ADS_CONFIG_DR_RATE_490              = 0x0040      #Data rate=490SPS
ADS_CONFIG_DR_RATE_920              = 0x0060      #Data rate=920SPS
ADS_CONFIG_DR_RATE_1600             = 0x0080      #Data rate=1600SPS
ADS_CONFIG_DR_RATE_2400             = 0x00A0      #Data rate=2400SPS
ADS_CONFIG_DR_RATE_3300             = 0x00C0      #Data rate=3300SPS
ADS_CONFIG_COMP_MODE_WINDOW         = 0x0010      #Comparator mode:Window comparator 比较器模式:窗口比较器
ADS_CONFIG_COMP_MODE_TRADITIONAL    = 0x0000      #Comparator mode:Traditional comparator (default) 比较器模式:传统比较器(默认)
ADS_CONFIG_COMP_POL_LOW             = 0x0000      #Comparator polarity:Active low (default) 比较器极性:低电平有效(默认)
ADS_CONFIG_COMP_POL_HIGH            = 0x0008      #Comparator polarity:Active high
ADS_CONFIG_COMP_LAT                 = 0x0004      #Latching comparator  锁存比较器
ADS_CONFIG_COMP_NONLAT              = 0x0000      #Nonlatching comparator (default) 无锁存
ADS_CONFIG_COMP_QUE_ONE             = 0x0000      #Assert after one conversion 一次转换后断言
ADS_CONFIG_COMP_QUE_TWO             = 0x0001      #Assert after two conversions 两次转换后断言
ADS_CONFIG_COMP_QUE_FOUR            = 0x0002      #Assert after four conversions 四次转换后断言
ADS_CONFIG_COMP_QUE_NON             = 0x0003      #Disable comparator and set ALERT/RDY pin to high-impedance (default) 禁用比较器并将ALERT/RDY引脚设置为高阻抗(默认)

Config_Set = 0

class ADS1015(object):
    def __init__(self,address=ADS_I2C_ADDRESS):
        self._address = address
        self._bus = smbus.SMBus(1)
    def ADS1015_SINGLE_READ(self,channel):                    #Read single channel data 读取单通道数据
        data=0
        Config_Set =  ( ADS_CONFIG_MODE_NOCONTINUOUS        |   #mode:Single-shot mode or power-down state    (default) 模式:单触发模式或掉电状态
                        ADS_CONFIG_PGA_4096                 |   #Gain= +/- 4.096V                              (default)
                        ADS_CONFIG_COMP_QUE_NON             |   #Disable comparator                            (default)
                        ADS_CONFIG_COMP_NONLAT              |   #Nonlatching comparator                        (default)
                        ADS_CONFIG_COMP_POL_LOW             |   #Comparator polarity:Active low               (default)
                        ADS_CONFIG_COMP_MODE_TRADITIONAL    |   #Traditional comparator                        (default)
                        ADS_CONFIG_DR_RATE_1600             )   #Data rate=1600SPS                             (default)
        if channel == 0:
            Config_Set |= ADS_CONFIG_MUX_SINGLE_0
        elif channel == 1:
            Config_Set |= ADS_CONFIG_MUX_SINGLE_1
        elif channel == 2:
            Config_Set |= ADS_CONFIG_MUX_SINGLE_2
        elif channel == 3:
            Config_Set |= ADS_CONFIG_MUX_SINGLE_3
        Config_Set |=ADS_CONFIG_OS_SINGLE_CONVERT
        self._write_word(ADS_POINTER_CONFIG,Config_Set)
        #time.sleep(0.01)
        data=self._read_u16(ADS_POINTER_CONVERT)>>4
        #print(data)
        return data
    def _read_u16(self,cmd):
        LSB = self._bus.read_byte_data(self._address,cmd)
        MSB = self._bus.read_byte_data(self._address,cmd+1)
        return (LSB << 8) + MSB
    def _write_word(self, cmd, val):
        Val_H=val&0xff
        Val_L=val>>8
        val=(Val_H<<8)|Val_L
        self._bus.write_word_data(self._address,cmd,val)

main.py代码中有个time.sleep(0.00986)是我看其他资料说灯有个10ms的周期,也不知道是不是这样的,最后结果感觉去掉也没什么不一样。
不过这个代码刚运行出来时数据都是(0)ug/m3。说明我的采样还是有问题的。

异常数据分析

因此笔者注意起了这个灯,灯貌似是看不见它有点亮的。还好笔者有个led灯模块测试了一下,排除了自身灯异常的问题。接下来是给出灯的测试代码(由于本身的0.28ms太小了,直接看灯是一直亮着的,因此每个延迟函数扩了100倍,能看到灯在闪)。
在这里插入图片描述
led.py:

# coding=UTF-8
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
IN1 = 16
GPIO.setwarnings(False)
GPIO.setup(IN1,GPIO.OUT)
while 1:
    GPIO.output(IN1,GPIO.HIGH)

    time.sleep(0.028)
    time.sleep(0.004) # 持续采集0.04ms
    GPIO.output(IN1,GPIO.LOW) # 关闭二级管灯
    time.sleep(0.986)

因此灯的代码编辑是没有问题的。所以我把注意力转移到了ADC采样上。接下来我只运行了灯的代码,有万用表测了一下AOUT与GND的电压。发现量程都到最低档了,也就只有周期性的一点的变化。
在这里插入图片描述
我的ADC采样模块虽然算是精度比较高的,但有可能因为笔者在室内,空气质量相对较好。测得的粉尘数值因此过于小,采样的时候没有捕捉到微量的变化。它既然是测粉尘质量的,那要是粉尘过大,数值应该就上来了。
于是,笔者插了根杜邦线进去。。。。
这一根下去,数据马上就发生了变化。数据直接达到了300左右。但是会一直维持在300值不变。于是笔者将杜邦线不规则的划来划去,数值也相应的发生了变化,如下图所示。
在这里插入图片描述
在上图中笔者发现293.83数值较多,后面笔者将一整个钥匙插进去,也是这么个数值。这可能是这个模块的阈值吧。查了查数据对照表,发现300也算是严重污染了。
在这里插入图片描述

数据分析:

整个记录做好以后,笔者做出了如下分析:
a.ADC采样精度不够,影响了该记录的大量时间。
b.有可能是这个模块GP2Y1010AU0F不是特别灵敏了,现在淘宝上还有GP2Y1014AU0F甚至GP2Y1015AU0F的。15版本是用串口收发数据了,不需要管led灯的事情,相对比较方便,这个csdn也有了对应的教程与代码。

bilibili上笔者也找到了这样一个视频[教程]70块钱搞定树莓派检测PM2.5和有害气体
在这里插入图片描述

里面用的ADC采样模块是ADS1115。笔者模块里集成的是ADS1015.他那里的相对较精准一些吧,而且里面有提供外国网站的ADS1115例程链接。
测PM2.5的模块则是GP2Y1014AU0F,可能也会比我这个精准一些,他视频里面出来的数据也相对较好。笔者既然现在已经买了这两个搭配,出来的数据也还一般,已经有笔者这样搭配的模块可以作为参考。

参考:

树莓派远程监控空气质量
用树莓派做PM2.5检测仪–欧姆龙篇
树莓派传感器模块Sense HAT (B)的使用 多传感器(大气压 温湿度 气体浓度ADC采样)集合 通过一个.py文件运行
基于树莓派的空气监测系统(3)PM2.5模块程序

总结:

现在笔者能出数据,说明模块内的ILED灯是正常的,但肉眼确实看不到。
硬件编程上数据出现问题排查起来还是非常困难的,其实我这个结果 一直都只是因为精度不够,数据其实还是可以出来的,离成功只差一根杜邦线而已。不过对问题的分析与排查还是需要在硬件与代码上综合分析。希望我的记录能对读者有一定的帮助。
感谢各位观看,如有不足,欢迎在评论内留言与讨论。如果觉得写得好的,可以给我点赞+收藏+关注哦,再次感谢各位!
在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐