Harun YenisanMehmet Emin Uğur
Published © MIT

Remote Air Pollution Monitoring for Industrial Zones

Remote Air Pollution Monitoring for Industrial Zones (VOC/Gas Focus)

IntermediateWork in progress24 hours40
Remote Air Pollution Monitoring for Industrial Zones

Things used in this project

Hardware components

RAKwireless RAK3372
×1
RAKwireless RAK19003
×1
RAKwireless RAK1906
×1
RAKwireless Solar Panel
×1
Hologram Global IoT SIM Card
Hologram Global IoT SIM Card
×1

Software apps and online services

Arduino IDE
Arduino IDE
Datacake
Datacake
SmartThings service
IFTTT SmartThings service

Hand tools and fabrication machines

Multitool, Screwdriver
Multitool, Screwdriver

Story

Read more

Code

Remote Air Pollution Monitoring for Industrial Zones

Arduino
Remote Air Pollution Monitoring for Industrial Zones
#include <WisBlock-api.h>      // For RAK WisBlock APIs
#include <TinyGsmClient.h>     // General header for TinyGSM library
#include <TinyGsmClientSIM7600.h> // Header for SIM7600/BG77 modem family used by RAK3372
#include <PubSubClient.h>      // For MQTT communication (with Datacake)
#include <Wire.h>              // For I2C communication (for BME680)
#include <Adafruit_Sensor.h>   // Base class for Adafruit sensor libraries
#include <Adafruit_BME680.h>   // For RAK1906 (uses BME680 sensor)

// MQTT and Datacake Settings
// These values must be obtained from your Datacake dashboard and device registrations.
const char* MQTT_BROKER = "mqtt.datacake.co"; // Datacake MQTT broker address
const int MQTT_PORT = 1883;                   // MQTT port (8883 can also be used for TLS)
const char* MQTT_USERNAME = "YOUR_DATACAKE_MQTT_USERNAME"; // <<< CHANGE THIS
const char* MQTT_PASSWORD = "YOUR_DATACAKE_MQTT_PASSWORD"; // <<< CHANGE THIS
const char* MQTT_CLIENT_ID = "YOUR_DATACAKE_MQTT_CLIENT_ID"; // <<< CHANGE THIS
const char* MQTT_TOPIC = "dtc/v3/YOUR_DATACAKE_DEVICE_UUID/measurement"; // <<< CHANGE THIS

// GPRS connection information
const char apn[]  = "YOUR_APN";       // <<< CHANGE THIS (Your mobile operator's APN)
const char gprsUser[] = "";           // APN username (usually left blank)
const char gprsPass[] = "";           // APN password (usually left blank)

// Hardware Objects
TinyGsm modem(Serial1); // Serial port for RAK3372 modem (UART1)
TinyGsmClient client(modem); // TinyGSM client (TCP connection via modem)
PubSubClient mqttClient(client); // MQTT client

Adafruit_BME680 bme; // RAK1906 (BME680) environmental sensor object

// Timer Variables
long lastSendTime = 0;
const long sendInterval = 300000; // Data transmission interval: 5 minutes (5 * 60 * 1000 ms)

void setup() {
  // Start serial communication (for debug messages)
  Serial.begin(115200);
  while (!Serial); // Wait for serial port to be ready
  Serial.println("Industrial Air Pollution Monitoring Starting...");

  // Initialize RAK1906 (BME680) sensor
  Wire.begin(); // Start I2C communication
  if (!bme.begin(0x76)) { // I2C address is typically 0x76 or 0x77.
    Serial.println("RAK1906 BME680 not found, check connection!");
    while (1); // Stay in infinite loop if initialization fails
  }
  // Sampling and gas heater settings for BME680 (for VOC detection)
  bme.setTemperatureOversampling(BME680_OS_8X);
  bme.setHumidityOversampling(BME680_OS_2X);
  bme.setGasHeater(320, 150); // Heater settings for gas sensor (320°C, 150ms)
  Serial.println("RAK1906 BME680 successfully initialized.");

  // Initialize RAK3372 modem
  Serial1.begin(115200); // Set UART1 speed for RAK3372
  delay(100); // Small delay for modem to start
  Serial.println("Modem initializing...");
  modem.restart(); // Restart modem
  Serial.print("Modem Info: ");
  Serial.println(modem.getModemInfo()); // Get modem information

  // Try to connect to GPRS network
  Serial.print("Connecting to GPRS network...");
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    Serial.println("GPRS connection failed! Please check APN settings and SIM card.");
    while (true); // Stay in infinite loop if connection fails
  }
  Serial.println("GPRS connection successful!");

  // Set MQTT server connection details (Datacake)
  mqttClient.setServer(MQTT_BROKER, MQTT_PORT);
  // Set MQTT Callback function (to handle commands from Datacake)
  mqttClient.setCallback(mqttCallback);

  connectMqtt(); // Try to connect to MQTT broker
  lastSendTime = millis(); // Initialize timer
}

void loop() {
  modem.maintainConnections(); // Keep GPRS connection active
  
  // Run MQTT client loop (to send and receive messages)
  if (!mqttClient.connected()) {
    connectMqtt(); // Try to reconnect if disconnected
  }
  mqttClient.loop();

  // Check if it's time to send data
  if (millis() - lastSendTime > sendInterval) {
    sendData();       // Send sensor data
    lastSendTime = millis(); // Reset timer
  }
}

// Function to manage connecting to MQTT broker
void connectMqtt() {
  Serial.print("Connecting to MQTT...");
  // Stay in loop until connection is successful
  while (!mqttClient.connected()) {
    // Connect to MQTT with client ID, username, and password (as required by Datacake)
    if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) {
      Serial.println("connected.");
      // If you expect to receive commands from Datacake, you can subscribe to the relevant topic here.
      // mqttClient.subscribe("dtc/v3/YOUR_DATACAKE_DEVICE_UUID/command"); 
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state()); // Print error code
      Serial.println(" trying again in 5 seconds");
      delay(5000); // Wait before retrying
    }
  }
}

// Callback function to process incoming messages (downlink) via MQTT
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message Received - Topic: ");
  Serial.println(topic);
  Serial.print("Message: ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  // You can add code here to make the device react to incoming commands.
}

// Function to collect sensor data and send it to Datacake
void sendData() {
  // Start BME680 reading
  if (!bme.performReading()) {
    Serial.println("BME680 reading failed!");
    return; // Exit function if reading fails
  }
  
  float temperature = bme.temperature; // Temperature value
  float humidity = bme.humidity;       // Humidity value
  float gas_resistance = bme.gas_resistance / 1000.0; // Gas resistance (Ohms to KiloOhms)

  // Print read data to Serial Monitor
  Serial.printf("Temperature: %.2f C, Humidity: %.2f %%RH, Gas Resistance: %.2f KOhms\n", temperature, humidity, gas_resistance);

  // Create a JSON formatted payload (in the format expected by Datacake)
  String jsonPayload = "{\"temperature\":";
  jsonPayload += String(temperature, 2); // 2 decimal places
  jsonPayload += ",\"humidity\":";
  jsonPayload += String(humidity, 2);   // 2 decimal places
  jsonPayload += ",\"gas_resistance\":";
  jsonPayload += String(gas_resistance, 2); // 2 decimal places
  jsonPayload += "}";

  Serial.print("Sending Data: ");
  Serial.println(jsonPayload);

  // Publish the created JSON payload to the relevant topic in Datacake
  if (mqttClient.publish(MQTT_TOPIC, jsonPayload.c_str())) {
    Serial.println("Data successfully sent to Datacake.");
  } else {
    Serial.println("Data sending failed!");
  }
}

Credits

Harun Yenisan
8 projects • 7 followers
Electronic engineer, working as an IT teacher at the Ministry of National Education.
Mehmet Emin Uğur
9 projects • 9 followers
Computer engineer, working as an IT teacher at the Ministry of National Education.

Comments