Team work : Budhi Prayoga,ST.M.Eng , Muhamad Setyo Adi,.A.M.d.T

0. Background Research

1. Dokumen

2. Alat dan Bahan

  • NodeMCu ESP8266 Lolin v3
  • PSU Adjustable Stepdown 3A LM2596S + LED Voltmeter
  • Contactless Temperature Sensor Module GY-906 DCI MLX90614ESF
  • Resistive Soil Moisture SEN-0016
  • LCD 20×4 Blue Light
  • i2c Module LCD Backpack 20×4
  • Active Continuous Buzzer HYD-2312 YD2312 type SFM-20B DC 3-24V
  • Bi Color LED 5mm (Red, Blue) Common Anoda, Cathode include Bracket / Holder LED 5mm
  • Bi Color LED 5mm (Red, Green) Common Anoda
  • Spacer LED 4x4mm
  • Antenna DB for ESP8266 WiFi Module
  • Box Project X5 Hitam Plastik 160x130x45 mm Case Enclosure for Project
  • Metal Push Button Switch Self Locked 12mm Ring LED 12V Waterproof
  • Rocker Switch 3 Pin KCD1 Merah 220v 13x95mm
  • Spacer Kuningan M3 6mm
  • Resistor 330ohm 1/4 watt Metal Film
  • Cable Spiral Band 6mm Putih
  • Cable Ties 25x100nn 4.0″ Merah
  • Kabel Jumper Female to Female, Male to Male, Male to Female 20cm

3. Skematik Rangkaian

4. Arduino Code

/*
 * # File Name      : Code Mr. M. Setyo A Beta v1
 * # Customer       : Mr. Muhammad Setyo Adi
 * # Judul Research : Monitoring Suhu dan Kelembaban Tanah
 *                    pada Proyek Konstruksi Jalan Raya
 *                    Menggunakan Robotdyn Arduino Uno Built-in
 *                    WiFi berbasis IoT Blynk dan Thinger.io
 * # Deskripsi      : Perangkat Elektronika yang digunakan                   
 *                    untuk memonitoring suhu dan kelembaban
 *                    tanah secara realtime terhubung ke internet
 *                    dengan media penyimpanan cloud dapat diakses
 *                    melalui android smartphone melalui aplikasi
 *                    Blynk dan Dashboard IoT 3rd Platform Thinger.io
 *                    pada Personal Computer / Laptop di Control Room
 * # Maker by       : Budhi Prayoga,.M.Eng                   
 * # Start Created  : 09 Maret 2023
 * # Last Update    : 11 Maret 2023 (2.0)
 *                  : 21 Juni 2023  (3.0)
 * # Revisi         : 1.0
 * # Version        : Beta
 * # IoT Platform   : Blynk, Thinger.io
 * # GPIO           :                   
 *    - Buzzer                  : 2 (D4 Lolin v3)
 *    - Sensor Suhu GY-906 DCI  : SCL D1, SDA D2
 *    - Sensor Kelembaban       : A0
 *    - LCD 20x4 I2C            : SCL D1, SDA D2
 * # Software Requirement
 *    - Arduino IDE v1.8.13
 * # Library
 *    - ESP8266 version   : 2.6.1 
 *    - Blynk version     : 1.1.0
 *    - Thinger version   : 2.15.0
 *    - MLX90614 version  : 2.1.3
 *    - Liquid Crystal    : 1.0.7
 * # Hardware Requirement
 *    - Main Core   : NodeMCU ESP8266 Lolin v3
 *    - Input       : Contacless Temperature Sensor GY-906 DCI MLX90614ESF 
 *                    Resistive Soil Moisture SEN-0016
 *    - Output      : LED 3mm + Holder
 *                    LCD 20x4 Blue Light
 *                    Active Buzzer 12VDC
 *    - Aksesoris   : Antenna DB for ESP8266 WiFi Module                
 * # Konfigurasi Upload Program
 *    Versi IDE     : 1.8.13
 *    Board         : NodeMCU 1.0(ESP12-E Module)
 *    Builtin LED   : 2
 *    Upload Speed  : 115200
 *    CPU Frequency : 80 MHz
 *    Flash Size    : 4MB (FS:2MB OTA:~1019KB)
 *    Reset Method  : dtr (aka NodeMCu)
 *    Debug Port    : disable
 *    Debug Level   : none
 *    lwlp variant  : v2 lower memory
 *    VTable        : Flash
 *    Exceptions    : Legacy (new can return nullptr)
 *    Erase Flash   : only sketch
 *    Espressif     : FW nonos-sdk-2.2.1+100
 *    SSL Support   : All SSL Chipher
 *    Programmer    : none
 *    Baudrate      : 115200
*/

#define BLYNK_AUTH_TOKEN "z7eItSK2_tdb3t7wsa-D4vm1Jna10Q3_"
#define BLYNK_PRINT Serial

#include <ESP8266WiFi.h
#include <BlynkSimpleEsp8266.h>
#include <ThingerESP8266.h>

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_MLX90614.h>

//konfigurasi Thinger.IO
#define USERNAME "userid"
#define DEVICE_ID "thingerid"
#define DEVIDE_CREDENTIAL "xxx"

//variable untuk thinger.io
ThingerESP8266 thing(USERNAME, DEVICE_ID, DEVIDE_CREDENTIAL);

char auth[] = BLYNK_AUTH_TOKEN;

char ssid[] = "xxx";  // type your wifi name
char pass[] = "xxx";  // type your wifi password

Adafruit_MLX90614 mlx = Adafruit_MLX90614();
LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x27 for a 16 chars and 2 line display

const int LCD_NB_ROWS = 4;
const int LCD_NB_COLUMNS = 20;

const int buzzer = 2;
const int SoilSensor = A0;

float temp_udara;
float temp_aspal;
float hum_sen0016;

float data1, data2, data3;

BlynkTimer timer;

/* Love Symbol characters */
byte customChar[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000,
  0b00000
};

/* Personalized characters */
byte START_DIV_0_OF_4[8] = {
  B01111,
  B11000,
  B10000,
  B10000,
  B10000,
  B10000,
  B11000,
  B01111
}; // Char début 0 / 4
byte START_DIV_1_OF_4[8] = {
  B01111,
  B11000,
  B10000,
  B10000,
  B10000,
  B10011,
  B11000,
  B01111
}; // Char début 1 / 4
byte START_DIV_2_OF_4[8] = {
  B01111,
  B11000,
  B10000,
  B10000,
  B10111,
  B10011,
  B11000,
  B01111
}; // Char début 2 / 4
byte START_DIV_3_OF_4[8] = {
  B01111,
  B11000,
  B10000,
  B10111,
  B10111,
  B10011,
  B11000,
  B01111
}; // Char début 3 / 4
byte START_DIV_4_OF_4[8] = {
  B01111,
  B11000,
  B10011,
  B10111,
  B10111,
  B10011,
  B11000,
  B01111
}; // Char début 4 / 4
byte DIV_0_OF_8[8] = {
  B11111,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B11111
}; // Char milieu 0 / 8
byte DIV_1_OF_8[8] = {
  B11111,
  B00000,
  B00000,
  B00000,
  B00000,
  B11000,
  B00000,
  B11111
}; // Char milieu 1 / 8
byte DIV_2_OF_8[8] = {
  B11111,
  B00000,
  B00000,
  B00000,
  B11000,
  B11000,
  B00000,
  B11111
}; // Char milieu 2 / 8
byte DIV_3_OF_8[8] = {
  B11111,
  B00000,
  B00000,
  B11000,
  B11000,
  B11000,
  B00000,
  B11111
}; // Char milieu 3 / 8
byte DIV_4_OF_8[8] = {
  B11111,
  B00000,
  B11000,
  B11000,
  B11000,
  B11000,
  B00000,
  B11111
}; // Char milieu 4 / 8
byte DIV_5_OF_8[8] = {
  B11111,
  B00000,
  B11000,
  B11000,
  B11000,
  B11011,
  B00000,
  B11111
}; // Char milieu 5 / 8
byte DIV_6_OF_8[8] = {
  B11111,
  B00000,
  B11000,
  B11000,
  B11011,
  B11011,
  B00000,
  B11111
}; // Char milieu 6 / 8
byte DIV_7_OF_8[8] = {
  B11111,
  B00000,
  B11000,
  B11011,
  B11011,
  B11011,
  B00000,
  B11111
}; // Char milieu 7 / 8
byte DIV_8_OF_8[8] = {
  B11111,
  B00000,
  B11011,
  B11011,
  B11011,
  B11011,
  B00000,
  B11111
}; // Char milieu 8 / 8
byte END_DIV_0_OF_4[8] = {
  B11110,
  B00011,
  B00001,
  B00001,
  B00001,
  B00001,
  B00011,
  B11110
}; // Char fin 0 / 4
byte END_DIV_1_OF_4[8] = {
  B11110,
  B00011,
  B00001,
  B00001,
  B00001,
  B11001,
  B00011,
  B11110
}; // Char fin 1 / 4
byte END_DIV_2_OF_4[8] = {
  B11110,
  B00011,
  B00001,
  B00001,
  B11101,
  B11001,
  B00011,
  B11110
}; // Char fin 2 / 4
byte END_DIV_3_OF_4[8] = {
  B11110,
  B00011,
  B00001,
  B11101,
  B11101,
  B11001,
  B00011,
  B11110
}; // Char fin 3 / 4
byte END_DIV_4_OF_4[8] = {
  B11110,
  B00011,
  B11001,
  B11101,
  B11101,
  B11001,
  B00011,
  B11110
}; // Char fin 4 / 4

String staticMessage = "Welcome Mr. M Setyo!";
String scrollingMessage = "Sistem Monitoring Suhu Tanah Pada Proyek Pengerukan Konstruksi Jalan Berbasis IoT (Blynk dan Thinger.IO)";

bool isRunning = true;

void setup() {
  Serial.begin(115200);

  lcd.init();                      // initialize the lcd
  lcd.backlight();

  setup_progressbar();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Connecting to SSID"));
  //lcd.print("Progress Connecting");

  pinMode(buzzer, OUTPUT);

  //hubungkan NodeMCU ke server thinger.io
  thing.add_wifi(ssid, pass);

  //kirim nilai sensor suhu GY-906-DCI ke Thinger.IO
  thing["GY-906-DCI-Env"] >> [](pson & out) {
    out["suhu_udara"] = data1;
  };
  //kirim nilai sensor suhu GY-906-DCI ke Thinger.IO
  thing["GY-906-DCI-Object"] >> [](pson & out) {
    out["suhu_aspal"] = data2;
  };
  //kirim nilai sensor kelembaban Resistive Soil Moisture SEN-0016 ke Thinger.IO
  thing["SEN-0016"] >> [](pson & out) {
    out["kelembaban"] = data3;
  };
  
  Blynk.begin(auth, ssid, pass, "blynk.cloud", 80);
  mlx.begin();
}
void loop() {

  thing.handle();
  
  if (isRunning) {
    play();
  }
  
  Blynk.run();
  timer.run();
}

void scrollMessage(int row, String message, int delayTime, int LCD_NB_COLUMNS) {
  for (int i = 0; i < LCD_NB_COLUMNS; i++) {
    message = " " + message;
  }
  message = message + " ";
  for (int position = 0; position < message.length(); position++) {
    lcd.setCursor(0, row);
    lcd.print(message.substring(position, position + LCD_NB_COLUMNS));
    delay(125);
  }
}
void animasiJudul() {
  lcd.setCursor(0, 0);
  lcd.print(staticMessage);
  lcd.setCursor(0, 2);
  //lcd.print(F("abcdefghijklmnopqrst"));
  lcd.createChar(0, customChar); // create a new custom character
  lcd.setCursor(1, 2); // move cursor to (2, 0)
  lcd.write((byte)0);  // print the custom char at (2, 0)
  lcd.print(F(" PT.KPP Indexim"));
  lcd.setCursor(18, 2); // move cursor to (2, 0)
  lcd.write((byte)0);  // print the custom char at (2, 0)
  lcd.setCursor(0, 3);
  lcd.print(F("- KALTIM JUNI 2023 -"));
  scrollMessage(1, scrollingMessage, 250, LCD_NB_COLUMNS);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F(" Start Main Program"));
  timer.setInterval(1000L, sendSensor);
}
void sendSensor() {
  // put your main code here, to run repeatedly:
  int hasilPembacaan = analogRead(SoilSensor);

  temp_udara  = mlx.readAmbientTempC();
  temp_aspal  = mlx.readObjectTempC();
  hum_sen0016 = (100 - ((hasilPembacaan / 1023.00) * 100));

  // Check if any reads failed and exit early (to try again).
  /*if (isnan(temp_udara) || isnan(temp_aspal)) {
    Serial.println(F("Sensor Suhu GY -906 DCI tidak terhubung!"));
    digitalWrite(buzzer, HIGH);
    delay(500);
    digitalWrite(buzzer, LOW);  //LED p4 OFF
    delay(500);
    lcd.setCursor(0, 0);
    lcd.print(F("Temp Udara "));
    lcd.setCursor(11, 0);
    lcd.print(F("Unconnect"));
    lcd.setCursor(0, 1);
    lcd.print(F("Temp Aspal "));
    lcd.setCursor(11, 1);
    lcd.print(F("Unconnect"));
    delayMicroseconds(1000000);  //Simple 1 Sec Delay
    lcd.clear();
    return;
  }
  if (isnan(hum_sen0016)) {
    Serial.println(F("Sensor Kelembaban SEN-0016 tidak terhubung!"));
    lcd.print(F("Sensor tidak Terhubung"));
    delay(2000);
    //delayMicroseconds(1000000);  //Simple 1 Sec Delay
    return;
    }*/

  Serial.println();
  Serial.print(F("Suhu Udara = "));
  Serial.print(temp_udara);
  Serial.print(F("°C\tSuhu Aspal = "));
  Serial.print(temp_aspal);
  Serial.println("°C");
  Serial.print(F("Persentase Kelembaban Tanah/Aspal= "));
  Serial.print(hum_sen0016);
  Serial.println(F("%"));

  if (mlx.readObjectTempC() > 100) {
    alarm();
  }

  Blynk.virtualWrite(V0, temp_udara);
  Blynk.virtualWrite(V1, temp_aspal);
  Blynk.virtualWrite(V2, hum_sen0016);

  data1 = temp_udara;
  data2 = temp_aspal;
  data3 = hum_sen0016;
  
  lcd.setCursor(0, 0);
  lcd.print(F("Temp Udara "));
  lcd.setCursor(11, 0);
  lcd.print(temp_udara);
  //lcd.setCursor(18,0);
  lcd.print((char)223); //Simbol Derajat di LCD
  lcd.print(F("C"));
  lcd.setCursor(0, 1);
  lcd.print(F("Temp Aspal "));
  lcd.setCursor(11, 1);
  lcd.print(temp_aspal);
  //lcd.setCursor(18,1);
  lcd.print((char)223); //Simbol Derajat di LCD
  lcd.print(F("C"));
  lcd.setCursor(0, 2);
  lcd.print(F("Kelembaban"));
  lcd.setCursor(11, 2);
  lcd.print(hum_sen0016);
  //lcd.setCursor(18,2);
  lcd.print(F(" %"));
  delay(1000);
  //lcd.clear();
}
void alarm() {
  //Blynk.email("[email protected]", "Alert", "Temperature over 50°C!");
  Blynk.logEvent("notifikasi", "Suhu diatas 50 Derajat celcius");
  Serial.print(F("Suhu aspal melebihi batas ambang 100°C"));
  lcd.setCursor(0, 3);
  lcd.print(F("Suhu aspal Overheat"));
  digitalWrite(buzzer, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(500);                       // wait for a second
  digitalWrite(buzzer, LOW);    // turn the LED off by making the voltage LOW
  delay(500);
  Serial.println();
  lcd.clear();
  delay(150);
  //lcd.clear();
}
void play() {
  /* Value in percent of the progress bar */
  static byte percent = 0;

  /* Displays the value */
  draw_progressbar(percent, 1);

  //for(static byte percent = 0; percent <= 101; percent++){
  /* Increases the percentage */
  if (++percent == 101) {
    // Revient à zéro si le pourcentage dépasse 100%
    //draw_progressbar(percent, 1);
    percent = 0;
    delay(5);
    WiFi.begin(ssid, pass);           //koneksi ke WiFi
    while (WiFi.status() != WL_CONNECTED) { //pengecekan koneksi WiFi
      Serial.print(".");
      lcd.setCursor(0, 2);
      lcd.print(F("."));
      delay(500);
    }
    mainprogram();
    isRunning = false;
  }

  /* Small waiting time */
  delay(1);
}
void mainprogram () {
  lcd.setCursor(0, 0);
  lcd.print(F("Device Connected to "));
  lcd.setCursor(0, 2);
  lcd.print(ssid);
  lcd.setCursor(0, 3);
  lcd.print(WiFi.localIP());
  delay(3000);
  lcd.clear();
  animasiJudul();
}
void setup_progressbar() {
  lcd.createChar(0, START_DIV_4_OF_4);
  lcd.createChar(1, DIV_0_OF_8);
  lcd.createChar(2, DIV_8_OF_8);
  lcd.createChar(3, END_DIV_0_OF_4);
}
void draw_progressbar(byte percent, byte line) {
  lcd.setCursor(0, line); //Move the cursor on the line

  /* Map the range ( 0 ~ 100 ) to the range ( 0 ~ ( LCD_NB_COLUMNS - 4 ) * 2 * 4 - 2 * 4 ) */
  byte nb_columns = map(percent, 0, 100, 0, (LCD_NB_COLUMNS - 4) * 2 * 4 - 2 * 4);
  // Each character displays 2 vertical bars of 4 pixels high, but the first and last character displays only one.

  /* Draw each character from the line */
  for (byte i = 0; i < LCD_NB_COLUMNS - 4; ++i) {

    if (i == 0) { // Premiére case

      /* Displays the start tank according to the number of columns */
      if (nb_columns > 4) {
        lcd.write((byte) 0); // Char début 4 / 4
        nb_columns -= 4;

      } else if (nb_columns == 4) {
        lcd.write((byte) 0); // Char début 4 / 4
        nb_columns = 0;

      } else {
        switch_progressbar_bank(0);
        lcd.setCursor(i, line);
        lcd.write(nb_columns + 4); // Char début N / 4
        nb_columns = 0;
      }

    }
    else if (i == LCD_NB_COLUMNS - 5) { // Derniére case

      /* Displays the end tank according to the number of columns */
      if (nb_columns > 0) {
        switch_progressbar_bank(3);
        lcd.setCursor(i, line);
        lcd.write(nb_columns + 3); // Char fin N / 4

      } else {
        lcd.write(3); // Char fin 0 / 4
      }

    }
    else { // Autres cases

      /* Displays the appropriate tank according to the number of columns */
      if (nb_columns == 0) {
        lcd.write(1); // Char div 0 / 8

      } else if (nb_columns >= 8) {
        lcd.write(2); // Char div 8 / 8
        nb_columns -= 8;

      } else if (nb_columns >= 4 && nb_columns < 8) {
        switch_progressbar_bank(2);
        lcd.setCursor(i, line);
        lcd.write(nb_columns); // Char div N / 8
        nb_columns = 0;

      } else if (nb_columns < 4) {
        switch_progressbar_bank(1);
        lcd.setCursor(i, line);
        lcd.write(nb_columns + 3); // Char div N / 8
        nb_columns = 0;
      }
    }
  }

  /* Displays the percentage */
  char tmp[5];
  sprintf(tmp, "%3d%%", percent);
  lcd.print(tmp);
}
void switch_progressbar_bank(byte bank) {
  // IMPORTANT: It is necessary to make a lcd.clear ( ) or a lcd.setCursor ( ) after each change of bank.
  /* Change of character bank */
  switch (bank) {
    case 0:
      lcd.createChar(4, START_DIV_0_OF_4);
      lcd.createChar(5, START_DIV_1_OF_4);
      lcd.createChar(6, START_DIV_2_OF_4);
      lcd.createChar(7, START_DIV_3_OF_4);
      break;

    case 1:
      lcd.createChar(4, DIV_1_OF_8);
      lcd.createChar(5, DIV_2_OF_8);
      lcd.createChar(6, DIV_3_OF_8);
      lcd.createChar(7, DIV_4_OF_8);
      break;

    case 2:
      lcd.createChar(4, DIV_4_OF_8);
      lcd.createChar(5, DIV_5_OF_8);
      lcd.createChar(6, DIV_6_OF_8);
      lcd.createChar(7, DIV_7_OF_8);
      break;

    case 3:
      lcd.createChar(4, END_DIV_1_OF_4);
      lcd.createChar(5, END_DIV_2_OF_4);
      lcd.createChar(6, END_DIV_3_OF_4);
      lcd.createChar(7, END_DIV_4_OF_4);
      break;
  }
  //lcd.setCursor(0,2);
  //lcd.print(F("switch_progressbar"));
}

5. Galeri

Demo IoT 3rd Platform Blynk (Desktop Version)

Isometric View

Top View

Front View

Right View

Left View

Back View

6. Data Hasil Percobaan

7. Video Demo

8. Kesimpulan