Air pollution is one of the largest environmental and public health challenges in the world today. Air pollution leads to adverse effects on our health, climate and ecosystem. Monitoring air quality is essential to understanding and preventing air pollution, as well as assessing emission sources in order to preserve a healthier air condition and contribute to the fight against the greenhouse effect.

This project aims to create two devices that are able to communicate with each other via LoRa wireless communication. There are many sensors that can be used to monitor air quality, but for this project we’ll focus on two the DSM501A dust sensor and MQ131 ozone gas sensor. These sensors will be connected to the first device with the Ra-02 LoRa module as the client transceiver. The second device will have the LoRa shield as the server transceiver and SIM800L as the gateway connection for ThingSpeak.

Hardware Used

Software Used

Libraries Used

Application Description

Ra-02 SX1278 LoRa Module

The Ra-02 LoRa module can be used for ultra-long distance spread spectrum communication, with compatible FSK (frequency shift keying) remote modulation and demodulation. This provides a solution for the traditional wireless design which has poor distance coverage, anti-interference and power consumption. The Ra-02 can be widely used in a variety of networking occasions such as automatic meter reading, home building automation, security systems, and remote irrigation systems.


  • Interface: SPI
  • Programmable Bit Rate: up to 300 kbps
  • Frequency Range: 410-525 MHz
  • Antenna: IPEX
  • Max Transmit Power: 18 +/- 1 dBm
  • Power Supply: 2.5~3.7 V, Typically 3.3 V
  • Operating Temperature: -30~85 °C

Dragino LoRa Shield

The Dragino LoRa Shield is a long range transceiver on a Arduino shield form factor and based on Open source library. The LoRa Shield allows the user to send data and reach extremely long ranges at low data-rates. It provides ultra-long range spread spectrum communication and high interference immunity whilst minimizing current consumption.


  • Maximum Link Budget: 168 dB
  • Programmable Bit Rate: up to 300 kbps
  • High Sensitivity: down to -148 dBm
  • Bullet-proof front end: IIP3 = -12.5 dBm
  • Rx Current: 10.3 mA
  • Register Retention: 200 nA
  • Synthesizer Resolution: 61 Hz
  • Modulation: FSK, GFSK, MSK, GMSK, LoRaTM and OOK
  • Dynamic Range RSSI: 127 dB

How Do They Work?

They use a technique called spread spectrum communication. Information is transmitted over a much wider bandwidth than is necessary. By doing so, strength against external narrowband interference is increased. Since the wider the bandwidth of any transmitted signal, the lower the relative influence of interference over a small part of the bandwidth will be. The spread spectrum techniques allow for simultaneous multiplexing of multiple transmissions in the same bandwidth. Since multiple users can share the same spread spectrum bandwidth without interfering with one another, these can be called as multiple access techniques.

You can learn more about spread spectrum communications by clicking here.


The SIM800L is a cellular module which allows for GPRS data transmission, sending and receiving SMS and making and receiving voice calls. The board is compact in size and has low current consumption. It even features a power saving technique which limits current consumption to as low as 1 mA when not in use. To top it off, the module supports quad-band GSM/GPRS network, meaning it works pretty much anywhere in the world.

How does it work?

The module is intended to operate similar to a cellular phone with a SIM card. After it is powered, the module boots up, searches for the nearest cellular network and connects to it automatically. The LED displays on the board indicate the connection state. It blinks fast when there’s no network coverage, but blinks slow when connected.

DSM501A Dust Sensor

The DSM501A dust sensor module is a low cost and compact particle density sensor. It is capable of quantitative particle measurement of fine particles as small as one micron

How does it work?

An infrared emitting diode and a phototransistor are diagonally arranged inside of the device. This allows it to detect the reflected light of dust in the surrounding air. It is especially effective in detecting very fine particles like cigarette smoke, and is commonly used in air purifier systems.

MQ131 Ozone Gas Sensor

The MQ131 Ozone Gas Sensor, as its name suggests, can sense ozone gas (O3). It has a high sensitivity to ozone while also being sensitive to strong oxides such as CL2 and NO2. These are widely used in air quality monitoring applications, whether it is domestic or industrial.

How does it work?

Inside the black bakelite of the ozone gas sensor is a rather sensitive material called Tungsten trioxide (WO3). Its conductivity is high in clean air, but when ozone gas is present in the surrounding, the conductivity gets lower. Users can convert the change of conductivity to correspond to the output signal for the gas concentration detected.

Hardware Setup

Client (Ra-02 LoRa Module with with Sensors)

Ra-02 LoRa Module Connections

  • 3.3V connected to Arduino 3.3V
  • RST connected to Arduino pin 9
  • DI00 connected to Arduino pin 2
  • GND connected to Arduino Ground
  • NSS connected to Arduino pin 10
  • MOSI connected to Arduino pin 11
  • MISO connected to Arduino pin 12
  • SCK connected to Arduino pin 13

DSM501A Dust Sensor Connections

  • 3rd Wire (VCC) connected to Arduino 5V
  • 4th Wire (PM2.5) connected to Arduino pin 7
  • 5th Wire (GND) connected to Arduino Ground

MQ131 Ozone Gas Sensor Connections

  • A0 connected to Arduino pin A0
  • VCC and GND connected to Arduino 5V and Ground respectively

Server (Dragino LoRa Shield with the SIM800L Module)

ThingSpeak Setup

To setup ThingSpeak, first you have to Sign up on their website If you already have an account, login and click on “Channels.”

In doing so, you should be directed to your channels page. Click on “New Channel” to create a new channel.

Once loaded, you will be presented with the Channel Settings. Fill up whatever is required and check 2 fields, one for each sensor reading display. You only need to give it a name and a description; the rest are unnecessary.

When you’re done, just scroll to the bottom of the page and hit “Save Channel.” After this, you will be able to view the display privately by clicking the “Private View” tab on top. You can customize the field charts by clicking the pencil icon.

You would also need to get the API keys for your Arduino code in order for the SIM800L to transmit the data to ThingSpeak. To check your API key, click on the “API Keys” tab at the top.


Client (Ra-02 LoRa Module with with Sensors)

Server (Dragino LoRa Shield with the SIM800L Module)

Code Breakdown

Client Code


Before we start, we must include the libraries to be used in the project. The RHReliableDatagram is for sending addressed, acknowledged, and retransmitted datagrams and is under the RH_RF95 library. These two libraries depend on the SPI (Serial Peripheral Interface) to function so that one is included as well. DSM501 and MQ131 libraries are for the dust sensor and ozone gas sensor respectively.

After that, we define the client address and server address. This is important to make sure you’re communicating one-on-one with the your LoRa shield and not any others which could be nearby. Next, we create the RF95 driver object and datagram manager with the driver and client address as the parameters.

Next, we assign the pins to the sensors. For the DSM501, the first parameter is for PM1.0 which we assigned to pin 6. The second parameter is for PM2.5. For the MQ131, the first parameter is for the heater which is already integrated into the module so you can assign it to any pin that’s not already used. The ozone gas sensor we are using is the black bakelite low concentration version so we indicate it on the third parameter. The 10000 is the load resistance. Lastly, we initialize the variable buf to store incoming messages.

void setup()

Inside the setup() function, we set the Serial baud rate to 9600 bps which is the default for serial communication. We then put an if statement to check if the reliable datagram manager has initialized or not by printing a message when it fails. After that, we initialize the DSM501 and MQ131. The base resistance of the MQ131 module was obtained by running o3sensor.calibrate() in code and the time to read is set to 30 seconds.

void loop()

Inside the loop() function, we grab the dust sensor reading with dsm501.update() and then get the particulate matter density of particles over 2.5 µm using dsm501.getParticleWeight(1). We then have to convert this float value to a string and store it in a variable at the same time using dtostrf().

This data can then be sent to the server-side using manager.sendtoWait(). This sends the message (with retries) and waits for an acknowledgement. It returns true if an acknowledgement is received which allows it to print the acknowledgement reply using manager.recvfromAckTimeout().

Next, we grab the ozone gas sensor reading with o3sensor.begin() and then get the value reading in µg/m3 using o3sensor.getO3(UG_M3). If you want to get the parts per billion which is more commonly used in global statistical data reports then you can do so with o3sensor.getO3(PBB) instead. Similar to the dust sensor reading above, we then have to convert the float value to a string and store it in a variable at the same time using dtostrf().

This part is similar to the one above where we send the sensor readings to the server-side, but this time it’s for the ozone gas sensor.

Server Code


Again, we must include the libraries to be used in the project. The SoftwareSerial library allows serial communication on other digital pins of the Arduino which allows us to use pins 2 and 3 as a receiver and transmitter respectively. This also allows us to use it as a debug serial and print out the message received later on.

After that, we define the client address and server address. Again, this is important to make sure you’re communicating one-on-one with the the Ra-02 and not any others which could be nearby so make sure they are the same with what’s in the client-side code. Next, we create the RF95 driver object and datagram manager with the driver and server address as the parameters.

Here, we create the message we want to send to the client when the server-side receives a message as well as initialize the variable buf to store the incoming sensor reading data. The index will be used later to distinguish between the two data values received.

void setup()

Inside the setup() function, we set the serial baud rate to 9600 bps which is the default for serial communication and the GSM serial baud rate to 4800 bps. After that, we put an if statement to check if the reliable datagram manager has initialized or not by printing a message when it fails.

SIM800L AT Commands for Sending Data to ThingSpeak

AT (ATtention) commands are instructions used to control a modem. Firstly, AT+CIPMODE=0 sets the TCPIP application mode to normal as this is necessary to govern the connection of the SIM800L to the Internet. Secondly, AT+CIPMUX=0 starts up a single IP connection and AT+CGATT=1 attaches us to the GPRS service. Thirdly, AT+CSTT=”APN” starts task and sets the access point name to the url specified and AT+CIICR brings up the wireless connections with GPRS. You can find the APN to use for the SIM card you have online.

Next, AT+CIFSR gets your local IP address and AT+CIPSTART=”TCP”,”″,”80″ will start up the TCP connection to ThingSpeak. Lastly, AT+CIPSEND=80 allows you to send data to ThingSpeak with about 80 characters length. This is where the GET command is run. Change the API key in the updateTS string; you have to make sure the API key in the link matches the one in your channel by checking the “API Keys” tab in your ThingSpeak channel. It’s also important to note that AT+CIPSHUT has to be sent by the end of all these to ensure that the GPRS is deactivated. If you don’t shut it down, it may time itself out.

void loop()

Inside the loop() function, we check for when there’s a message received with manager.available() and if index is 1, that means it’s the first sensor’s data. recvfromAck() will get the data from the buffer and print it in the serial monitor. Once done, it will call the gsmSend() function which contains all the AT commands to send the data to ThingSpeak then set the index to 2 so it knows the second sensor’s data is next to come.

This part is similar to the one above where we send the data to ThingSpeak, but this time it’s for the second received data which is from the second sensor.


LoRa is a low data rate, low power, and low power wireless platform technology for building IoT networks. It is more cost efficient in the long run compared to other alternatives due to its wide list of features. The transceivers can communicate in up to 15 kilometers distance away from each other which is really far, often more than enough for many applications. LoRa Technology operates in the unlicensed ISM band, therefore is legal and great for educational purposes.

The sensors I used for this project were a bit harder to test due to the difficulty of finding values to compare. One has to be very specific in searching online for air quality monitoring value standards for dust particulate matter and ozone gas concentration. Luckily, the libraries we have for both sensors handle the calculation for us.

Sensor Reading Comparison

These are the guidelines or standards defined by the World Health Organization for dust particulate matter PM2.5 and Ground-Level Ozone Gas:

To get the AQI (Air Quality Index) for comparison, you can use an online AQI calculator.


Tags : Air Quality MonitoringDragino LoRa ShieldDSM501DSM501ADust SensorGPRSGSMLoRaMQ131Ozone Gas SensorRa-02RemoteRF95RH_RF95RHReliableDatagramSIM800LSpread Spectrum CommunicationSX1278Thingspeak

Leave a Response