Kutluhan Aktar
Published © CC BY

Jigglypuff IoT Carbon Dioxide and Dust Monitor w/ Telegram

Based on Arduino Nano RP2040 Connect, display the dust density (mg/m3) and the carbon dioxide (CO2) density (ppm) via a Telegram bot.

ExpertFull instructions provided6 hours5,236
Jigglypuff IoT Carbon Dioxide and Dust Monitor w/ Telegram

Things used in this project

Hardware components

PCBWay Custom PCB
PCBWay Custom PCB
×1
Arduino Nano RP2040 Connect
Arduino Nano RP2040 Connect
×1
MH-Z14A NDIR CO2 Sensor
×1
GP2Y1010AU0F Dust Sensor
×1
SSD1306 OLED (128x32)
×1
SparkFun 5 mm Common Anode RGB LED
×1
Capacitor, 220 µF
Capacitor, 220 µF
×1
Through Hole Resistor, 150 ohm
Through Hole Resistor, 150 ohm
×1
Through Hole Resistor, 2.2 kohm
Through Hole Resistor, 2.2 kohm
×1
Through Hole Resistor, 3.3 kohm
Through Hole Resistor, 3.3 kohm
×1
DC Power Connector, Jack
DC Power Connector, Jack
×1
External Battery
×1

Software apps and online services

Arduino IDE
Arduino IDE
KiCad
KiCad
Visual Studio 2017
Microsoft Visual Studio 2017

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Custom parts and enclosures

Gerber Files

Fabrication Files

Schematics

PCB_1

PCB_2

PCB_3

PCB_4

Code

Jigglypuff_Carbon_Dioxide_and_Dust_Monitor_and_Tracker.ino

Arduino
         /////////////////////////////////////////////  
        //  Jigglypuff IoT Carbon Dioxide and Dust //
       //      Monitor (Tracker) w/ Telegram      //
      //             ---------------             //
     //      (Arduino Nano RP2040 Connect)      //           
    //             by Kutluhan Aktar           // 
   //                                         //
  /////////////////////////////////////////////

//
// Based on Arduino Nano RP2040 Connect, display the dust density (mg/m3) and the carbon dioxide (CO2) density (ppm) via a Telegram bot.
//
// For more information:
// https://www.theamplituhedron.com/projects/Jigglypuff_IoT_Carbon_Dioxide_and_Dust_Monitor_Tracker_w_Telegram/
//
//
// Connections
// Arduino Nano RP2040 Connect :  
//                                MH-Z14A NDIR CO2 Sensor
// D2  --------------------------- PWM
// RX  --------------------------- TX (Optional) 
// TX  --------------------------- RX (Optional)
//                                GP2Y1010AU0F Dust Sensor
// D4  --------------------------- LED
// A0  --------------------------- S
//                                SSD1306 OLED 128x32
// A4  --------------------------- SDA
// A5  --------------------------- SCL
//                                5mm Common Anode RGB LED
// D3  --------------------------- R
// D5  --------------------------- G
// D6  --------------------------- B   


// Include the required libraries.
#include <SPI.h>
#include <WiFiNINA.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

char ssid[] = "<____>";    // your network SSID (name)
char pass[] = "<____>";    // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;          // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;

// Change the server below before running the code.
char server[] = "www.theamplituhedron.com";

// Define the web application path.
String application = "/telegram_co2_and_dust_monitor/";

// Initialize the Ethernet client library.
WiFiSSLClient client; /* WiFiClient client; */

// Define the SSD1306 screen settings:
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino reset pin)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Define the MH-Z14A NDIR CO2 Sensor settings:
#define MH_Z14A_PWM 2

// Define the timer for the MH-Z14A NDIR CO2 Sensor:
unsigned long heat_timer = 0;
unsigned long timer = 0;

// Define the data holders:
int co2_level = 0;

// Define the GP2Y1010AU0F Dust Sensor pins (measure and LED) and settings.
int measurePin = A0;
int ledPower = 4;
float voMeasured = 0;
float calcVoltage = 0;
float dustDensity = 0;
float calibration = 0.05;

// Define RGB pins:
#define redPin 3
#define greenPin 5
#define bluePin 6

void setup() {
  // RGB:
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  adjustColor(0,0,0);

  pinMode(ledPower,OUTPUT);
  pinMode(MH_Z14A_PWM, INPUT_PULLUP);

  // Start the timer:
  heat_timer = millis();

  // Initialize the SSD1306 screen:
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();                                
  display.setTextSize(2);
  display.setCursor(0,0);                              
  display.setTextColor(SSD1306_WHITE);      
  display.println("Heating &");
  display.print("Connecting");
  display.display();
  delay(1000);

  // Check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) { adjustColor(255,0,0); while (true); }
  // Attempt to connect to the WiFi network:
  while (status != WL_CONNECTED) {
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);
    // Wait 10 seconds for connection:
    delay(10000);
  }
  // If connected to the network successfully:
  adjustColor(0,0,255);
  
}

void loop() {
  // Wait until the MH-Z14A NDIR CO2 Sensor heats for 5 minutes.
  while (millis() - heat_timer < 300000){ if (millis() - timer > 1000){ timer = millis(); } }
  // Get readings from the CO2 Sensor every 20 seconds.
  adjustColor(255,0,255);
  if (millis() - timer > 20000){ MH_Z14A_readings(); timer = millis(); make_a_get_request(); }
  
  // Elicit the dust density generated by the GP2Y1010AU0F dust sensor.
  GP2Y10_dust_sensor_readings();

  display.clearDisplay();                                
  display.setTextSize(1);
  display.setCursor(0,0);                              
  display.setTextColor(SSD1306_WHITE);      
  display.print("Dust: ");
  display.print(dustDensity);
  display.print(" mg/m3");
  display.print("\nCO2: ");
  display.print(co2_level);
  display.print(" ppm");
  display.display();

}

void GP2Y10_dust_sensor_readings(){
  // Calculate the voltage.
  digitalWrite(ledPower,LOW);
  delayMicroseconds(280);
  voMeasured = analogRead(measurePin);
  delayMicroseconds(40);
  digitalWrite(ledPower,HIGH);
  delayMicroseconds(9680);
  // Evaluate the dust density (mg/m3).
  calcVoltage = voMeasured*(3.3/1024);
  dustDensity = 0.17*calcVoltage-0.1;
  // Calibrate data:
  if(dustDensity < 0){ dustDensity = dustDensity + calibration; }
  delay(1000);
}

void MH_Z14A_readings(){
  // Elicit the CO2 density generated by the CO2 Dust Sensor (PWM):
  while (digitalRead(MH_Z14A_PWM) == LOW) {};
  long t0 = millis();
  while (digitalRead(MH_Z14A_PWM) == HIGH) {};
  long t1 = millis();
  while (digitalRead(MH_Z14A_PWM) == LOW) {};
  long t2 = millis();
  long tH = t1-t0;
  long tL = t2-t1;
  long ppm = 5000L * (tH - 2) / (tH + tL - 4);
  while (digitalRead(MH_Z14A_PWM) == HIGH) {};
  co2_level = (int)ppm;
  delay(10);
}

void make_a_get_request(){
  // Connect to the web application named telegram_co2_and_dust_monitor. Change '443' with '80' if you are not using SSL connection.
  if (client.connect(server, 443)){
    // If successful:
    adjustColor(255,255,0);
    // Create the query string:
    String query = application + "?co2="+co2_level+"&dust="+dustDensity;
    // Make an HTTP Get request:
    client.println("GET " + query + " HTTP/1.1");
    client.println("Host: www.theamplituhedron.com");
    client.println("Connection: close");
    client.println();
  }else{
    adjustColor(255,0,0);
  }
  delay(2000); // Wait 2 seconds after connection...
  // If there are incoming bytes available, get the response from the web application.
  String response = "";
  while (client.available()) { char c = client.read(); response += c; }
  if(response != "" && response.indexOf("Data Registered Successfully!") > 0){
    adjustColor(0,255,0);
    display.clearDisplay();                                
    display.setTextSize(1);
    display.setCursor(0,0);                              
    display.setTextColor(SSD1306_WHITE);      
    display.println("Data Registered");
    display.println("Successfully!");
    display.display();
  }
}

void adjustColor(int r, int g, int b){
  analogWrite(redPin, (255-r));
  analogWrite(greenPin, (255-g));
  analogWrite(bluePin, (255-b));
}

index.php

PHP
Web Application (telegram_co2_and_dust_monitor)
<?php

// Define the telegram_co2_dust_monitor class and its functions:
class telegram_co2_dust_monitor {
	public $token, $web_path, $conn, $table;
	
	public function __init__($token, $server, $conn, $table){
		$this->token = $token;
		$this->web_path = $server.$token;
		$this->conn = $conn;
		$this->table = $table;
	}
	// Telegram:
	public function send_message($id, $string){
		$new_message = $this->web_path."/sendMessage?chat_id=".$id."&text=".urlencode($string);
		file_get_contents($new_message);
	}
	
	public function send_photo($id, $photo, $caption){
	    $new_photo = $this->web_path."/sendPhoto?chat_id=".$id."&photo=".$photo."&caption=".$caption;
	    file_get_contents($new_photo);
	}

	public function send_video($id, $video, $caption){
	    $new_video = $this->web_path."/sendVideo?chat_id=".$id."&video=".$video."&caption=".$caption;
	    file_get_contents($new_video);
	}
	
	// Database Save:
	public function update_database($column, $value){
		$sql = "UPDATE `$this->table` SET `$column`='$value' LIMIT 1";
		mysqli_query($this->conn, $sql);
	}
	
	// Database Fetch:
	public function get_data_from_database($_column){
		$sql = "SELECT * FROM `$this->table` LIMIT 1";
		$result = mysqli_query($this->conn, $sql);
		if($row = mysqli_fetch_assoc($result)){
			return $row[$_column];
		}
	}
}

// Define database and server settings:
$server = array(
	"name" => "localhost",
	"username" => "<__>",
	"password" => "<__>",
	"database" => "telegramco2dustmonitor",
	"table" => "entries"

);

$conn = mysqli_connect($server["name"], $server["username"], $server["password"], $server["database"]);

// Define the new 'co2_dust_monitor' object:
$co2_dust_monitor = new telegram_co2_dust_monitor();
$bot_token = "<________________________>"; // e.g., 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
$co2_dust_monitor->__init__($bot_token, "https://api.telegram.org/bot", $conn, $server["table"]); 

// Get updates from the Telegram Bot API.
$updates = json_decode(file_get_contents('php://input'), TRUE); 
if($updates['update_id']){
	$chat_id =   $updates['message']['chat']['id'];
	$message = $updates['message']['text'];
    
	if($updates["message"]["photo"]){
		$co2_dust_monitor->send_message($chat_id, "Thank you for sending me a photo but I cannot process it yet ");
	}else if($updates["message"]["video"]){
		$co2_dust_monitor->send_message($chat_id, "Thank you for sending me a video but I cannot process it yet  ");
	}else if($updates["message"]["document"]){
		$co2_dust_monitor->send_message($chat_id, "Thank you for sending me a file but I cannot process it yet  ");
	}else{
		// Commands:
		switch($message){
		  case '/start':
		  $co2_dust_monitor->update_database("chat_id", $chat_id); // Register the chat ID to send messages without an update by the bot. 
		  $co2_dust_monitor->send_message($chat_id, "Chat ID has been successfully registered to the application database. \n\nEnter /help to view all available commands.");
		  break;	
		  case '/co2_density':
		  $co2 = $co2_dust_monitor->get_data_from_database("co2");
		  $date = $co2_dust_monitor->get_data_from_database("date");
		  $co2_dust_monitor->send_message($chat_id, " CO2 Density  $co2 ppm\n\n Last Update: $date");
		  break;	
		  case '/dust_density':
		  $dust = $co2_dust_monitor->get_data_from_database("dust");
		  $date = $co2_dust_monitor->get_data_from_database("date");
		  $co2_dust_monitor->send_message($chat_id, " Dust Density  $dust mg/m3\n\n Last Update: $date");
		  break;
		  case '/help':
		  $co2_dust_monitor->send_message($chat_id, "/co2_density - display the CO2 density generated by the MH-Z14A NDIR CO2 Sensor\n/dust_density - monitor the dust density produced by the GP2Y10 Dust Sensor");
		  break;
          default:
          $co2_dust_monitor->send_message($chat_id, "Waiting for your commands :)");
          break;		  
	    }
	}
}

// If transferred by the device (Nano RP2040 Connect), save incoming data to the database.
if(isset($_GET["co2"]) && isset($_GET["dust"])){
	$co2_dust_monitor->update_database("co2", htmlentities($_GET["co2"]));
	$co2_dust_monitor->update_database("dust", htmlentities($_GET["dust"]));
	// Set timezone:
	date_default_timezone_set("Europe/London");
	$co2_dust_monitor->update_database("date", date("Y-m-d___H:i:s"));
	echo("Data Registered Successfully!");
}

?>

Credits

Kutluhan Aktar

Kutluhan Aktar

65 projects • 206 followers
Self-Taught Full-Stack Developer | @EdgeImpulse Ambassador | Maker | Physics Enthusiast

Comments