Introduction: Track Air Quality Using Grafana and Raspberry Pi

About: Just someone organising ones and zeros

I was looking for a small little IOT project and a friend recommended I check out this tutorial:

https://dzone.com/articles/raspberry-pi-iot-sensor...

I highly recommend following the tutorial to follow on in setting up a Raspberry Pi for monitoring. This tutorial will complete further steps in the design of a simple IoT device that allows for a high error tolerance, as well as how useful a Raspberry Pi can be when paired up with Arduino.

I also go into the effectiveness and somewhat limitation of the MQ* models of air sensors. MQ* sensors are cheap and fairly effective, and are super easy to set up.

Overall this will help you to get started in connecting an Arduino to the internet in the simplest way possible, and sets the way for using lighter footprint modules (re: ESP8266).

Have fun!

Step 1: Equipment + Set Up

Equipment

  • Raspberry Pi with Raspbian installed
  • Raspberry Pi power supply
  • Arduino Uno/Equivalent
  • Male to male USB type B to Type A (should come with your Arduino)
  • Any of MQ* sensors (I used the MQ-2, 4, 5, and 135)
  • Assorted jumper wires
  • mini breadboard

Set up

This tutorial is meant as a gentle intro to using Arduino and Raspberry Pi - it will help to know how to use the linux terminal. However, I don't assume much experience with working on Arduino or Raspberry Pi - all you really need is the equipment provided and a curious attitude.

  • You will need to complete the steps in this tutorial.
  • I recommend you use Secure Shell (SSH) to interface with the Raspberry Pi, as this allows you to input commands easily. Connecting via SSH differs whether you are using Windows, Linux or Mac. Linux and Mac are pretty easy to use in regards to ssh (the command to open SSH is literally ssh). Check out Putty for Windows. I recommend you check out screen as a way to maintain your session during the project.
  • You will also need to install Python on Raspbian. When I completed these steps, I made a copy of an old SD card I had lying around from a previous project, which already had Python installed. If your distribution of NOOBS/Raspbian doesn't have Python 3.7 or above, check out these steps to compile Python from source.
  • Get acquainted with git and install it in case it's not already installed in your distribution of Raspbian.

Step 2: Setting Up the Circuit

There is one circuit you'll need to set up in the Arduino.

I've provided a schematic that you can use as a reference.

The beauty of all of the MQ-* gas sensors is that once a 5 Volt and Ground connection are made, the input resistance of the Arduino's analog pins allows the sensor to work correctly.

Be careful to ensure that the Analog connection from the breakout board in the sensor is connected to the Arduino and not the digital connection. If you are facing a very narrow range of values when testing, I recommend you check your connection here first.

Step 3: Arduino Code and Flashing

In the step following this one, we'll be connecting the Arduino board to the Raspberry Pi. Before we do this, we need to flash the Arduino with code to read the sensor as well as transmit sensor data to the Raspberry Pi. This can be done in any way that you normally push code to the Arduino. I used a third party tool apart from the Arduino IDE - hence, I include the Arduino library at the top. This is not necessary for other projects.

Check the code to copy/paste at the end of this section.

What does the code do

The code is set up to obtain data from four different sensors - if you use different types of sensors, it will be sensible to change the namings on the output signal sent from the Serial port.

In the loop we check if the Raspberry Pi requests data from us. Hence, we are using a very simple Master/Slave configuration where the Raspberry Pi will continually make requests to the Arduino for data. This is much simpler than having a counter in the Arduino code because it's easier to test what values work from the Raspberry Pi, instead of having to flash new values to the Arduino.

The arduino, once received a request for data, will format the output as a GET parameter - this is related to HTTP methods and is simply a design choice. If you were to design a communication schema from Arduino via Serial Port, you could easily go for anything else, as long as you design it so data is reasonably separated. I chose GET because its familiar and robust.

Simple testing...

Once you have the Arduino flashed and code running, open the Arduino IDE's Serial Monitor. If you send the single character "H" (ensure its capital!) you will get the payload of data. Congrats, it works!

A sample, asynchronous collector of MQ-* data

#include<Arduino.h>
int mq2 = A2;
int mq4 = A3;
int mq5 = A4;
int mq135 = A5;
int incomingByte;
voidsetup() {
pinMode(mq2, INPUT);
pinMode(mq4, INPUT);
pinMode(mq5, INPUT);
pinMode(mq135, INPUT);
Serial.begin(9600);
}
/* valuePrint prints the value for this label.
* Creates side effects only.
*/
voidvaluePrint(String label, int reading) {
Serial.print(label);
Serial.print("=");
Serial.print(reading);
}
voidloop() {
// see if there's incoming serial data:
if (Serial.available() >0) {
// read the oldest byte in the serial buffer:
// "When you call Serial.read a byte is removed from the receive buffer and returned to your code"
incomingByte = Serial.read();
// if it's a capital H (ASCII 72), read the values and send them to the raspberry host.
// TODO: ensure the message is always the same length, each time
if (incomingByte == 72) {
int mq2Reading = analogRead(mq2);
int mq4Reading = analogRead(mq4);
int mq5Reading = analogRead(mq5);
int mq135Reading = analogRead(mq135);
Serial.print("?");
valuePrint("mq2", mq2Reading);
Serial.print("&");
valuePrint("mq4", mq4Reading);
Serial.print("&");
valuePrint("mq5", mq5Reading);
Serial.print("&");
valuePrint("mq135", mq135Reading);
Serial.print("\n");
}
}
// read the serial only every second
delay(1000);
}
view rawmain.cpp hosted with ❤ by GitHub

Step 4: Raspberry Pi Code

Now that you've configured the Raspberry Pi as per https://dzone.com/articles/raspberry-pi-iot-sensor... , you can now run the Raspberry Client code that will send data via MQTT to our database, which also connects to Grafana.

  1. Make sure your raspberry is connected to the internet and then perform a git clone command to copy the whole code to the Raspberry Pi. Your command will look a little bit like:
    git clone  https://github.com/luiszugasti/air_sens.git
    .
  2. Within the raspberry Pi's terminal, perform a change directory command (cd) into "raspberry_client":
    cd raspberry_client
    .
  3. You will need to use a virtual environment*. Simple. Run
    python3 -m venv env
    . This will create a virtual environment called "env" which we will use to install dependencies.
  4. Now, we need to enter our virtual environment. Run:
    source env/bin/activate
    . You're now ready to install the dependencies of the project.
  5. In the package you just cloned there is a file called requirements.txt. Open this file; you will see we require the paho-mqtt and pyserial packages, as well as their respective versions. You can view the contents of the file by running
    cat requirements.txt
    . In order to install these packages, run
    pip install -r requirements.txt
    .
  6. This wraps up the configuration.

Literally every tutorial that uses python makes a mention of Virtual env, and even for this small project, I'll make a mention. Virtual environments allow you to separate versions of dependencies, as well as separate your python workflow - It's a nice way of tidying up your Python workspaces. If this is your first time using virtual environments, have a brief read up on them here.

What does the code do...

The client.py file will import a simple set of libraries including our own arduinosensor. In the main function, we will get the values from the Arduino, publish the data to the MQTT broker, and then sleep for 10 seconds.

The arduinosensor.py file is a set of helper methods that wrap around the paho.mqtt library, as well as provide some useful communication schema for communicating with the Arduino's payload (see: parse_payload). Of course, the code is attached at the end of this section.

A simple client that communicates with an arduino item via Serial monitor. Expect to find the code here when it goes public: https://github.com/luiszugasti/air_sens

fromimportlibimportimport_module
importos
importtime
importarduinosensor
defmain():
# open defined client
start_time=time.time()
whileTrue:
reading=arduinosensor.get_values(os.environ.get('PORT', "/dev/ttyUSB0"))
arduinosensor.pub("python_client", payload=reading)
time.sleep(10.0- ((time.time() -start_time) %10.0))
if__name__=="__main__":
main()
view rawclient.py hosted with ❤ by GitHub

Step 5: Putting It All Together

We have the Raspberry Python code set up, and we have the Arduino client code set up. Let's move on to connecting both entities together.

First, let's connect the Arduino and set up the correct configuration:

  1. On your Raspberry Pi terminal, run
    python -m serial.tools.list_ports
    . This will list all the USB ports that support serial communication.
  2. Now, plug in your Arduino and wait about 2 seconds for the Raspberry to recognise it. Typing in
    python -m serial.tools.list_ports
    once more will show you the ports again. You may see an additional listing show up - if that is indeed the case, then this new entry is the entry that your Arduino is connected in. This is likely going to be "/dev/ttyUSB0".
  3. Try running the python code within your virtual environment by running python3.7 client.py. Wait a few seconds (at most ten) - if you face an exception, this means that we'll have to change the value for our com port on the raspberry pi. If you see that the code prints a line starting with "Sent following payload:..." Then, you will be good to go on to the final step with Grafana. Tip: make sure to run
    screen -S python
    before you start the python client, otherwise, when you end your connection to your raspberry pi, you will lose your running python program. Technically, you don't need to strictly use "python" as the last parameter, but I like naming my screen sessions accordingly.
    1. In order to change the value for the COM port, you will have to set an environment variable before running the code. You'll have to try this for every possible value of output you got when running python -m serial.tools.list_ports. For example, if the amount of entries I obtained were two, and were the following:
      • /dev/ttyUSB6
      • /dev/acm0

then the commands I'd run would be:

PORT="/dev/ttyUSB6" python3.7 client.py
, and if that were not to work, I'd subsequently run
PORT="/dev/acm0" python3.7 client.py
.

Once you've completed these steps, the code will commit data to our influxdb database instance which, when connected to Grafana, will allow us to view our dashboard.

Step 6: Grafana Configuration and Dashboard Viewing

Alright, we are now in the final stretch! We will now use Grafana to create a simple dashboard.

  1. Connect to your Grafana instance. Since you followed the steps from the original dzone article, you should be able to log in with your administrator user. Go ahead and login.
  2. On the left pane, hover over the "dashboards" icon - the four squares. Click on "Manage".
  3. On the new page, click on "New Dashboard". Further, click "Add new panel".
  4. This opens the Grafana editor. We'll create a simple view showing a single metric.
    1. In the right pane, change the panel title to something meaningful, such as "Kitchen Readings". You may also enter an optional Description.
    2. On the bottom left, "Query", we'll add a single time series. Grafana really shines here as we can easily create SQL statements with a click based interface. Under "default", choose InfluxDB.
    3. Now, for reading "A" - in the FROM clause, select measurement "airtestt". If you look at the original python code in the get_values function of arduinosensor.py, you'll see that we define this airtestt table within the code.
    4. For a sample, let us go to "SELECT" clause and choose field(mq4). Originally our dashboard will give us the choice "mean ()" - click on this choice and select "Remove". then, click on the plus sign and, under "Aggregations" choose "distinct()". This will show specific time points. We can choose other measures but for now, our panel will show distinct readings from mq4.
    5. Click Save on the top right, and you are done!

In case you run into trouble, you can verify your settings with those in the attached screenshot.

Step 7: Wrap Up

In this tutorial you were able to set up a robust MQTT network composed of a single node and broker. You were also able to visualize your IOT data using Grafana. Lastly, you were able to compose this simple system architecture from (hopefully) the comfort of your browser and PC via use of an SSH connection.

There's some things we may want to improve upon.

I'll leave some further reading to entice your imagination with the world of IOT. I look forward to seeing you in the next instructable!

Further readings: