Air quality monitor using GP2Y1010AU0F sensor on micropython based NodeMCU

I had purchased GP2Y1010AU0F sensor about 6 months back. Finally, found time to hook it with NodeMCU controller. Firstly, let me list things you need.

  • NodeMCU (ESP8266) controller (~300₹).
  • GP2Y1010AU0F dust/smoke sensor(~450₹).
  • 150k ohm resistor.
  • 220 µF capacitor.
  • breadboard with some connecting wires.

Here is the connection schema of circuit.

Since solution is based on micropython firmware on ESP8266, you should first read this.

Controller collects dust density from sensor on A0 analog pin. Transmits data to IoT broker over WiFi using MQTT protocol.

Main code on controller reads key configuration parameters from config.json file.


{
   "wifi": { "username": "SSID", "password": "WiFi Password" },
   "mqtt": { "broker": "broker IP/host", "mqttuser": "iotuser", "mqttpass": "iotpass", "mqttport": 1883, "mqttclient": "dustSensor0"},
   "sensors": {
                "dust": { "topic": "dustdensity", "Pin": 4, "wait": 600 }
              },
   "location": {"city": "ggn"}
}

Here is the python code, main.py on controller, that reads data from sensor.


from machine import Pin, ADC
import json
import time

with open('config.json') as cfg:
  cfgdata = json.load(cfg)

def connectWiFi():
    import network
    username = cfgdata.get('wifi').get('username')
    password = cfgdata.get('wifi').get('password')

    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('connecting to network...')
        sta_if.active(True)
        sta_if.connect(username, password)
        while not sta_if.isconnected():
            pass
    print('network config:', sta_if.ifconfig())

def mqttpublish(topic, message):
    from umqtt.simple import MQTTClient
    broker = cfgdata.get('mqtt').get('broker')
    mqttuser = cfgdata.get('mqtt').get('mqttuser')
    mqttpass = cfgdata.get('mqtt').get('mqttpass')
    mqttport = cfgdata.get('mqtt').get('mqttport')
    mqttclientID = cfgdata.get('mqtt').get('mqttclient')
    client = MQTTClient(mqttclientID, broker, port=mqttport, user=mqttuser, password=mqttpass)
    client.connect()
    print("sending....", topic, json.dumps(message))
    client.publish(topic, json.dumps(message))

def dustSensor(location):
    dustPin = ADC(0)
    voMeasured = dustPin.read()
    #print("Out", voMeasured)
    calcVoltage = voMeasured * (3.3 / 1024)
    dustDensity = 0.17 * calcVoltage - 0.01
    topic = cfgdata.get('sensors').get('dust').get('topic')
    wait = cfgdata.get('sensors').get('dust').get('wait')
    #print(voMeasured, calcVoltage, dustDensity)
    #message = {"dust": dustDensity, "location": location}
    message = {"tags": {"location": location}, "fields": {"dust": dustDensity}}
    return(message, topic, wait)

while True:
    location = cfgdata.get('location').get('city')
    connectWiFi()
    message, topic, wait = dustSensor(location)
    mqttpublish(topic, message)
    time.sleep(wait)

There is a MQTT subscriber connected to IoT broker which reads data from MQTT publisher, ie NodeMCU. MQTT subscriber listens on “dustdensity” topic and injects data into influxdb.

Just like on controller, I have prepared a config.json of MQTT subscriber on Raspberry Pi.


{
   "mqtt": { "broker": "127.0.0.1", "mqttuser": "iotuser", "mqttpass": "iotpass", "mqttport": 1883, "mqttclient": "Col0" },
   "topics": ["weather", "dustdensity"],
   "dataconn": {"dbhost": "127.0.0.1", "dbname": "iotdata", "dbuser": "iotdbuser", "dbpass": "iotdbpass"},
   "BaseLocation": {"city": "ggn"}
}

MQTT subscriber code reads config parameters from above json. Here is mqttcol.py code on Raspberry Pi.


#!/usr/bin/python3
import paho.mqtt.client as paho
import datetime
import time
import logging
import json
from influxdb import InfluxDBClient

with open('config.json') as cfg:
  cfgdata = json.load(cfg)

broker = cfgdata.get('mqtt').get('broker')
port = cfgdata.get('mqtt').get('mqttport')
username = cfgdata.get('mqtt').get('mqttuser')
password = cfgdata.get('mqtt').get('mqttpass')
topics = cfgdata.get('topics')
dbhost = cfgdata.get('dataconn').get('dbhost')
dbname = cfgdata.get('dataconn').get('dbname')
dbuser = cfgdata.get('dataconn').get('dbuser')
dbpass = cfgdata.get('dataconn').get('dbpass')

print(broker, port, username, password, topics, dbhost, dbname, dbuser, dbpass)
dbclient = InfluxDBClient(dbhost, 8086, dbuser, dbpass, dbname)

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)                                                                               
handler = logging.FileHandler('/var/log/mqttcol.log')  # create a file handler
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') # create a logging format
handler.setFormatter(formatter)
logger.addHandler(handler)# add the handlers to the logger

def on_subscribe(client, userdata, mid, granted_qos):
    #print("Subscribed: "+str(mid)+" "+str(granted_qos))
    print("Waiting for message...")

def on_message(client, userdata, mqttmsg):
    print(mqttmsg.topic+" "+str(mqttmsg.qos)+" "+mqttmsg.payload.decode("utf-8"))

    if mqttmsg.topic in topics:
         #client = InfluxDBClient(dbhost, 8086, dbuser, dbpass, dbname)
         dbmeasurement = mqttmsg.topic
         jsondata=json.loads(mqttmsg.payload.decode("utf-8"))
         jsondata['measurement']=dbmeasurement
         #jsondata['time']=str(datetime.datetime.now())
         payload=[jsondata]
         print(payload)
         dbclient.write_points(payload)
         logger.info(payload)

client = paho.Client()
client.username_pw_set(username, password)
client.on_subscribe = on_subscribe
client.on_message = on_message
client.connect(broker, port)
for topic in topics:
   client.subscribe(topic)

client.loop_forever()

Further, collected data can be plotted into Grafana.