// https://wokwi.com/projects/327381547863769683 (motor, library AccelStepper)
// https://wokwi.com/projects/327324886912467538 (motor, library Stepper)
// https://wokwi.com/projects/327360139702043220 (motor tanpa library)
// https://wokwi.com/projects/344891772964438612 (lcd)
// https://wokwi.com/projects/294590769009787402 (loading screen - lcd)
// https://wokwi.com/projects/424318978955716609 (sensor asap)
// https://docs.arduino.cc/language-reference/en/functions/analog-io/analogRead/ (Analog Read)
// https://wokwi.com/projects/288278249939665421 (2 LED + Switch)
// https://wokwi.com/projects/292033853022798344 (2 beep LED + Switch)
// https://wokwi.com/projects/288276100805558797 (1 LED + Switch)
// https://docs.wokwi.com/vscode/getting-started (Wokwi for VSCode)
// https://www.ardutech.com/2-lcd-i2c-arduino/ (ubah lcd)
// https://www.youtube.com/watch?v=orRmcVO_3C0 (cek alamat lcd)
// https://wokwi.com/projects/377640430885451777 (waktu dalam lcd)
// https://wokwi.com/projects/317578590427284033 (analogValue to celcius)
// https://docs.wokwi.com/parts/wokwi-ntc-temperature-sensor (analogValue to celcius)
// https://wokwi.com/projects/394150183321063425 (analogValue to ppm)
// https://www.ecmag.com/magazine/articles/article-detail/integrated-systems-fire-alarm-system-too-loud (Alarm 520hz)
// https://wokwi.com/projects/413539421911045121 (buzzer)
// https://community.platformio.org/t/cant-find-the-right-esp32-devkitc-v4/35393 (esp32 Env)
// https://mischianti.org/esp32-devkitc-v4-high-resolution-pinout-and-specs/#google_vignette (esp32 ports)
// https://randomnerdtutorials.com/esp32-useful-wi-fi-functions-arduino/ (Connection to ESP32)
// https://docs.platformio.org/en/stable/projectconf/sections/env/options/build/build_src_filter.html (build_src_filter)
// https://mischianti.org/esp32-devkitc-v4-high-resolution-pinout-and-specs/ (ESP C V4 Pins)
// https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32/_images/esp32_devkitC_v4_pinlayout.png (ESP C V4 Pins)
// https://www.hnhcart.com/blogs/learn/difference-between-arduino-uno-and-arduino-leonardo (Arduino Uno Pins)
// https://wokwi.com/projects/387807955020863489 (.py)
// https://thingsboard.io/docs/devices-library/esp32-dev-kit-v4/ (ESP32 C-V4 to THINGSBOARD)
/* ----- DECLARATION ----- */
#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // LCD I2C
#include <Stepper.h> // stepper motor (for exhaust fan)
// Sensor
#define Sensor_Gas_Bocor 35
#define Sensor_Asap 34
#define Sensor_Suhu 33
float gas;
float smoke;
float temperature;
//
// Aktuator
// LCD
#define I2C_ADDR_1 0x27
#define I2C_ADDR_2 0x26
#define LCD_COLUMNS 20
#define LCD_LINES 4
LiquidCrystal_I2C lcd_sensor(I2C_ADDR_1, LCD_COLUMNS, LCD_LINES);
LiquidCrystal_I2C lcd_aktuator(I2C_ADDR_2, LCD_COLUMNS, LCD_LINES);
// LED
int indikator1 = 0;
int indikator2 = 2;
int indikator3 = 4;
int indikator4 = 15;
int indikator5 = 12;
int indikator_danger = 13;
// Switch
int switch1 = 32;
// Buzz
int buzzer = 5;
// Exhaust Fan
#define Am 25
#define Ap 26
#define Bp 27
#define Bm 14
const int stepsPerRevolution = 200;
Stepper exhaust_fan(stepsPerRevolution, Bm, Bp, Ap, Am);
//
// Loading Screen
uint8_t pacman[8] = {
0b00000,
0b00000,
0b01110,
0b11011,
0b11111,
0b01110,
0b00000,
0b00000
};
uint8_t pacmanOpen[] = {
0b00000,
0b00000,
0b01110,
0b11011,
0b11100,
0b01110,
0b00000,
0b00000
};
uint8_t dot[] = {
0b00000,
0b00000,
0b00000,
0b00110,
0b00110,
0b00000,
0b00000,
0b00000
};
//
int itr = 0;
/* INTEGRATION TO THINGSBOARD */
// THINGSBOARD ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
#include <WiFi.h>
#include <ThingsBoard.h>
#include <Arduino_MQTT_Client.h>
#include <Server_Side_RPC.h>
#include <Attribute_Request.h>
#include <Shared_Attribute_Update.h>
constexpr char WIFI_SSID[] = "Wokwi-GUEST"; // default
constexpr char WIFI_PASSWORD[] = ""; // default
// See https://thingsboard.io/docs/getting-started-guides/helloworld/
// to understand how to obtain an access token
constexpr char TOKEN[] = "tV9zTFesz3FUxzfm9oLn";
// Thingsboard we want to establish a connection too
constexpr char THINGSBOARD_SERVER[] = "thingsboard.cloud"; // default
// MQTT port used to communicate with the server, 1883 is the default unencrypted MQTT port.
constexpr uint16_t THINGSBOARD_PORT = 1883U; // default
// Maximum size packets will ever be sent or received by the underlying MQTT client,
// if the size is to small messages might not be sent or received messages will be discarded
constexpr uint32_t MAX_MESSAGE_SIZE = 1024U; // default
// Baud rate for the debugging serial connection.
// If the Serial output is mangled, ensure to change the monitor speed accordingly to this variable
constexpr uint32_t SERIAL_DEBUG_BAUD = 115200U; // default
// Maximum amount of attributs we can request or subscribe, has to be set both in the ThingsBoard template list and Attribute_Request_Callback template list
// and should be the same as the amount of variables in the passed array. If it is less not all variables will be requested or subscribed
constexpr size_t MAX_ATTRIBUTES = 3U; // default
constexpr uint64_t REQUEST_TIMEOUT_MICROSECONDS = 5000U * 1000U;
// Attribute names for attribute request and attribute updates functionality
constexpr const char BLINKING_INTERVAL_ATTR[] = "blinkingInterval";
constexpr const char LED_MODE_ATTR[] = "ledMode";
constexpr const char LED_STATE_ATTR[] = "ledState";
// Initialize underlying client, used to establish a connection
WiFiClient wifiClient;
// Initalize the Mqtt client instance
Arduino_MQTT_Client mqttClient(wifiClient);
// Initialize used apis
Server_Side_RPC<3U, 5U> rpc;
Attribute_Request<2U, MAX_ATTRIBUTES> attr_request;
Shared_Attribute_Update<3U, MAX_ATTRIBUTES> shared_update;
const std::array<IAPI_Implementation*, 3U> apis = {
&rpc,
&attr_request,
&shared_update
};
// Initialize ThingsBoard instance with the maximum needed buffer size, stack size and the apis we want to use
ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE, Default_Max_Stack_Size, apis);
// handle led state and mode changes
volatile bool attributesChanged = false;
// LED modes: 0 - continious state, 1 - blinking
volatile int ledMode = 0;
// Current led state
volatile bool ledState = false;
// Settings for interval in blinking mode
constexpr uint16_t BLINKING_INTERVAL_MS_MIN = 10U;
constexpr uint16_t BLINKING_INTERVAL_MS_MAX = 60000U;
volatile uint16_t blinkingInterval = 1000U;
uint32_t previousStateChange;
// For telemetry
constexpr int16_t telemetrySendInterval = 10000U;
uint32_t previousDataSend;
// List of shared attributes for subscribing to their updates
constexpr std::array<const char *, 2U> SHARED_ATTRIBUTES_LIST = {
LED_STATE_ATTR,
BLINKING_INTERVAL_ATTR
};
// List of client attributes for requesting them (Using to initialize device states)
constexpr std::array<const char *, 1U> CLIENT_ATTRIBUTES_LIST = {
LED_MODE_ATTR
};
/// @brief Initalizes WiFi connection,
// will endlessly delay until a connection has been successfully established
void InitWiFi() {
Serial.print("Connecting to Access Point...");
// Attempting to establish a connection to the given WiFi network
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int i = 1;
while ((WiFi.status() != WL_CONNECTED) and (i < 20)) {
// Delay 500ms until a connection has been succesfully established
delay(500);
Serial.print(".");
i++;
}
Serial.println(" ");
if (WiFi.status() == WL_CONNECTED) {
Serial.println("Connected to Access Point");
} else {
Serial.println("Cannot Connected to Access Point, Try Again Later...");
}
}
/// @brief Reconnects the WiFi uses InitWiFi if the connection has been removed
/// @return Returns true as soon as a connection has been established again
const bool reconnect() {
// Check to ensure we aren't connected yet
const wl_status_t status = WiFi.status();
if (status == WL_CONNECTED) {
return true;
}
// If we aren't establish a new connection to the given WiFi network
InitWiFi();
return true;
}
/// @brief Processes function for RPC call "setLedMode"
/// RPC_Data is a JSON variant, that can be queried using operator[]
/// See https://arduinojson.org/v5/api/jsonvariant/subscript/ for more details
/// @param data Data containing the rpc data that was called and its current value
void processSetLedMode(const JsonVariantConst &data, JsonDocument &response) {
Serial.println("Received the set led state RPC method");
// Process data
int new_mode = data;
Serial.print("Mode to change: ");
Serial.println(new_mode);
StaticJsonDocument<1> response_doc;
if (new_mode != 0 && new_mode != 1) {
response_doc["error"] = "Unknown mode!";
response.set(response_doc);
return;
}
ledMode = new_mode;
attributesChanged = true;
// Returning current mode
response_doc["newMode"] = (int)ledMode;
response.set(response_doc);
}
// Optional, keep subscribed shared attributes empty instead,
// and the callback will be called for every shared attribute changed on the device,
// instead of only the one that were entered instead
const std::array<RPC_Callback, 1U> callbacks = {
RPC_Callback{ "setLedMode", processSetLedMode }
};
/// @brief Update callback that will be called as soon as one of the provided shared attributes changes value,
/// if none are provided we subscribe to any shared attribute change instead
/// @param data Data containing the shared attributes that were changed and their current value
void processSharedAttributes(const JsonObjectConst &data) {
for (auto it = data.begin(); it != data.end(); ++it) {
if (strcmp(it->key().c_str(), BLINKING_INTERVAL_ATTR) == 0) {
const uint16_t new_interval = it->value().as<uint16_t>();
if (new_interval >= BLINKING_INTERVAL_MS_MIN && new_interval <= BLINKING_INTERVAL_MS_MAX) {
blinkingInterval = new_interval;
Serial.print("Blinking interval is set to: ");
Serial.println(new_interval);
}
}
}
attributesChanged = true;
}
void processClientAttributes(const JsonObjectConst &data) {
for (auto it = data.begin(); it != data.end(); ++it) {
if (strcmp(it->key().c_str(), LED_MODE_ATTR) == 0) {
const uint16_t new_mode = it->value().as<uint16_t>();
ledMode = new_mode;
}
}
}
// Attribute request did not receive a response in the expected amount of microseconds
void requestTimedOut() {
Serial.printf("Attribute request timed out did not receive a response in (%llu) microseconds. Ensure client is connected to the MQTT broker and that the keys actually exist on the target device\n", REQUEST_TIMEOUT_MICROSECONDS);
}
const Shared_Attribute_Callback<MAX_ATTRIBUTES> attributes_callback(&processSharedAttributes, SHARED_ATTRIBUTES_LIST.cbegin(), SHARED_ATTRIBUTES_LIST.cend());
const Attribute_Request_Callback<MAX_ATTRIBUTES> attribute_shared_request_callback(&processSharedAttributes, REQUEST_TIMEOUT_MICROSECONDS, &requestTimedOut, SHARED_ATTRIBUTES_LIST);
const Attribute_Request_Callback<MAX_ATTRIBUTES> attribute_client_request_callback(&processClientAttributes, REQUEST_TIMEOUT_MICROSECONDS, &requestTimedOut, CLIENT_ATTRIBUTES_LIST);
// ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
/* ----- FUNCTION ----- */
void setUpThingsboard() {
if ((!tb.connected()) and (WiFi.status() == WL_CONNECTED)) {
// Bisa ditampilin di lcd juga
// Connect to the ThingsBoard
Serial.print("Connecting to: ");
Serial.print(THINGSBOARD_SERVER);
Serial.print(" with token ");
Serial.println(TOKEN);
if (!tb.connect(THINGSBOARD_SERVER, TOKEN, THINGSBOARD_PORT)) {
Serial.println("Failed to connect");
tb.sendTelemetryData("status_perangkat", "Disconnected");
return;
}
// Sending a MAC address as an attribute
tb.sendAttributeData("macAddress", WiFi.macAddress().c_str());
Serial.println("Subscribing for RPC...");
// Perform a subscription. All consequent data processing will happen in
// processSetLedState() and processSetLedMode() functions,
// as denoted by callbacks array.
if (!rpc.RPC_Subscribe(callbacks.cbegin(), callbacks.cend())) {
Serial.println("Failed to subscribe for RPC");
tb.sendTelemetryData("status_perangkat", "Disconnected");
return;
}
if (!shared_update.Shared_Attributes_Subscribe(attributes_callback)) {
Serial.println("Failed to subscribe for shared attribute updates");
tb.sendTelemetryData("status_perangkat", "Disconnected");
return;
}
Serial.println("Subscribe done");
tb.sendTelemetryData("status_perangkat", "Online");
// Request current states of shared attributes
if (!attr_request.Shared_Attributes_Request(attribute_shared_request_callback)) {
Serial.println("Failed to request for shared attributes");
return;
}
// Request current states of client attributes
if (!attr_request.Client_Attributes_Request(attribute_client_request_callback)) {
Serial.println("Failed to request for client attributes");
return;
}
}
}
void sendBuzzerToThingsboard(const char* buzzer) {
if (tb.connected()) {
tb.sendTelemetryData("buzzer", buzzer); // "Nyala", "Mati"
}
}
void sendLedToThingsboard(String led, String status) {
if (tb.connected()) {
tb.sendTelemetryData("led", led.c_str()); // Indikator apa yang nyala/mati
tb.sendTelemetryData("status_led", status.c_str()); // "Nyala", "Mati"
}
}
void sendExhaustFanToThingsboard(String exhaust_fan, int fan_speed) {
if (tb.connected()) {
tb.sendTelemetryData("exhaust_fan", exhaust_fan.c_str()); // "Nyala", "Mati"
tb.sendTelemetryData("fan_speed", fan_speed); // Kecepatan kipas
}
}
void sendGasStatusToThingsboard(const char* status_gas) {
if (tb.connected()) {
tb.sendTelemetryData("status_gas", status_gas); // "Berbahaya", "Normal", etc
}
}
void sendSmokeStatusToThingsboard(const char* status_smoke) {
if (tb.connected()) {
tb.sendTelemetryData("status_smoke", status_smoke); // "Berbahaya", "Normal", etc
}
}
void sendTemperatureStatusToThingsboard(const char* status_temperature) {
if (tb.connected()) {
tb.sendTelemetryData("status_temperature", status_temperature); // "Berbahaya", "Normal", etc
}
}
void sendTelemetryToThingsboard(float gas, float smoke, float temperature) {
if (tb.connected()) {
tb.sendTelemetryData("gas", gas); // nilai gas (tp masih analogvalue)
tb.sendTelemetryData("smoke", smoke); // nilai smoke (tp masih analogvalue)
tb.sendTelemetryData("temperature", temperature); // nilai temperature
tb.sendAttributeData("rssi", WiFi.RSSI());
tb.sendAttributeData("channel", WiFi.channel());
tb.sendAttributeData("bssid", WiFi.BSSIDstr().c_str());
tb.sendAttributeData("localIp", WiFi.localIP().toString().c_str());
tb.sendAttributeData("ssid", WiFi.SSID().c_str());
}
}
void sendQuoteToThingsboard() {
const char* quotes[5] = {
"Data is not only the data, it's the life of the people.",
"Api tidak pernah memilih waktu untuk muncul, tetapi manusia selalu punya pilihan untuk mencegahnya.",
"Kebakaran sering berawal dari hal kecil yang diabaikan; kewaspadaan selalu lebih murah daripada penyesalan.",
"Tidak ada api yang terlalu kecil untuk diabaikan, karena setiap percikan membawa potensi untuk mengubah segalanya.",
"Kebakaran mengingatkan bahwa ketelitian adalah tameng terpenting ketika risiko tidak terlihat."
};
int index = random(0, 5);
if (tb.connected()) {
tb.sendTelemetryData("quote", quotes[index]);
}
}
float hitung_celcius(int nilai_analog) {
const float BETA = 3950; // should match the Beta Coefficient of the thermistor
float celcius = 1 / (log(1 / (4095.0 / nilai_analog - 1)) / BETA + 1 / 298.15) - 273.15;
return celcius;
}
float hitung_ppm(int jenis, int nilai) {
/* PERHITUNGAN ASLI ANALOG VALUE TO PPM */
// https://bisaioti.com/pembacaan-sensor-dan-aktuator-dengan-adc-dac-pada-esp32/
// https://electronics.stackexchange.com/questions/388548/how-to-calculate-gas-sensor-ppm-from-analog-readings
// https://labrobotika.com/2025/07/31/cara-memprogram-sensor-tds-pada-esp32/
if (jenis == 0) { // asap (gas monoksida)
if (nilai >= 3693) {
return 650;
} if ((nilai >= 3578) and (nilai < 3693)) {
return (301 + 600)/2;
} if ((nilai >= 3436) and (nilai < 3578)) {
return (151 + 300)/2;
} if ((nilai >= 3146) and (nilai < 3436)) {
return (50 + 150)/2;
} if ((nilai > 843) and (nilai < 3146)) {
return (1 + 49)/2;
} if (nilai <= 843) {
return 0;
}
}
if (jenis == 1) { // gas bocor (Campuran propana dan butana)
if (nilai >= 3457) {
return 200;
} if ((nilai >= 3382) and (nilai < 3457)) {
return (121 + 160)/2;
} if ((nilai >= 3289) and (nilai < 3382)) {
return (81 + 120)/2;
} if ((nilai >= 3076) and (nilai < 3289)) {
return (40 + 80)/2;
} if ((nilai > 843) and (nilai < 3076)) {
return (1 + 39)/2;
} if (nilai <= 843) {
return 0;
}
}
return 0;
}
void rule_based(int jenis, int nilai) {
lcd_aktuator.init();
lcd_aktuator.setCursor(1, 0);
if (jenis == 0) { // asap (gas monoksida)
lcd_sensor.setCursor(12, 1);
lcd_sensor.print(" ");
lcd_sensor.setCursor(12, 1);
int fan_speed;
if (nilai >= 3693) {
lcd_sensor.print(">600 ppm");
lcd_aktuator.print("Asap Kebakaran");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: EVAKUASI!!");
fan_speed = 480;
exhaust_fan.setSpeed(fan_speed); // speed exhaust fan
exhaust_fan.step(stepsPerRevolution);
sendSmokeStatusToThingsboard("Asap Kebakaran (EVAKUASI!!)");
sendExhaustFanToThingsboard("Nyala", fan_speed);
} if ((nilai >= 3578) and (nilai < 3693)) {
lcd_sensor.print(">300 ppm");
lcd_aktuator.print("Asap Tebal");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Berbahaya");
fan_speed = 240;
exhaust_fan.setSpeed(fan_speed); // speed exhaust fan
exhaust_fan.step(stepsPerRevolution);
sendSmokeStatusToThingsboard("Asap Tebal (Berbahaya)");
sendExhaustFanToThingsboard("Nyala", fan_speed);
} if ((nilai >= 3436) and (nilai < 3578)) {
lcd_sensor.print(">150 ppm");
lcd_aktuator.print("Asap Tipis");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Sedikit");
lcd_aktuator.setCursor(9, 2);
lcd_aktuator.print("Berbahaya");
fan_speed = 120;
exhaust_fan.setSpeed(fan_speed); // speed exhaust fan
exhaust_fan.step(stepsPerRevolution);
sendSmokeStatusToThingsboard("Asap Tipis (Sedikit Berbahaya)");
sendExhaustFanToThingsboard("Nyala", fan_speed);
} if ((nilai >= 3146) and (nilai < 3436)) {
lcd_sensor.print("~50 ppm");
lcd_aktuator.print("Normal");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Normal");
fan_speed = 60;
exhaust_fan.setSpeed(fan_speed); // speed exhaust fan
exhaust_fan.step(stepsPerRevolution);
sendSmokeStatusToThingsboard("Normal (Normal)");
sendExhaustFanToThingsboard("Nyala", fan_speed);
} if ((nilai > 843) and (nilai < 3146)) {
lcd_sensor.print("<50 ppm");
lcd_aktuator.print("Tidak Masak");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Aman");
fan_speed = 0;
sendSmokeStatusToThingsboard("Tidak Masak (Aman)");
sendExhaustFanToThingsboard("Mati", fan_speed);
} if (nilai <= 843) {
lcd_sensor.print("0 ppm");
lcd_aktuator.print("Tak Ada Asap");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Aman");
fan_speed = 0;
sendSmokeStatusToThingsboard("Tak Ada Asap (Aman)");
sendExhaustFanToThingsboard("Mati", fan_speed);
}
}
if (jenis == 1) { // gas bocor (Campuran propana dan butana)
lcd_sensor.setCursor(12, 2);
lcd_sensor.print(" ");
lcd_sensor.setCursor(12, 2);
if (nilai >= 3457) {
lcd_sensor.print(">160 ppm");
lcd_aktuator.print("Ada Kebocoran Gas");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: EVAKUASI!!");
sendGasStatusToThingsboard("Ada Kebocoran Gas (EVAKUASI!!)");
} if ((nilai >= 3382) and (nilai < 3457)) {
lcd_sensor.print(">120 ppm");
lcd_aktuator.print("Ada Kebocoran Gas");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Sangat");
lcd_aktuator.setCursor(9, 2);
lcd_aktuator.print("Berbahaya");
sendGasStatusToThingsboard("Ada Kebocoran Gas (Sangat Berbahaya)");
} if ((nilai >= 3289) and (nilai < 3382)) {
lcd_sensor.print(">80 ppm");
lcd_aktuator.print("Ada Kebocoran Gas");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Berbahaya");
sendGasStatusToThingsboard("Ada Kebocoran Gas (Berbahaya)");
} if ((nilai >= 3076) and (nilai < 3289)) {
lcd_sensor.print("~40 ppm");
lcd_aktuator.print("Ada Kebocoran Gas");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Sedikit");
lcd_aktuator.setCursor(9, 2);
lcd_aktuator.print("Berbahaya");
sendGasStatusToThingsboard("Ada Kebocoran Gas (Sedikit Berbahaya)");
} if ((nilai > 843) and (nilai < 3076)) {
lcd_sensor.print("<40 ppm");
lcd_aktuator.print("Ada Kebocoran Gas");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Tidak");
lcd_aktuator.setCursor(9, 2);
lcd_aktuator.print("Normal");
sendGasStatusToThingsboard("Ada Kebocoran Gas (Tidak Normal)");
} if (nilai <= 843) {
lcd_sensor.print("0 ppm");
lcd_aktuator.print("Gas Tak Bocor");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Aman");
sendGasStatusToThingsboard("Gas Tak Bocor (Aman)");
}
}
if (jenis == 2) { // suhu
String led;
if ((digitalRead(indikator5) == LOW) and nilai >= 100) {
digitalWrite(indikator5, HIGH);
digitalWrite(indikator4, HIGH);
digitalWrite(indikator3, HIGH);
digitalWrite(indikator2, HIGH);
digitalWrite(indikator1, HIGH);
lcd_aktuator.print("Peningkatan Suhu");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: EKSTREM!!");
lcd_aktuator.setCursor(1, 2);
lcd_aktuator.print("Lampu 100% Nyala");
led = "indikator5";
sendTemperatureStatusToThingsboard("EKSTREM!!");
} if ((digitalRead(indikator4) == LOW) and nilai > 79) {
digitalWrite(indikator4, HIGH);
digitalWrite(indikator3, HIGH);
digitalWrite(indikator2, HIGH);
digitalWrite(indikator1, HIGH);
lcd_aktuator.print("Peningkatan Suhu");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Bahaya");
lcd_aktuator.setCursor(1, 2);
lcd_aktuator.print("Lampu 75% Nyala");
led = "indikator4";
sendTemperatureStatusToThingsboard("Bahaya");
} if ((digitalRead(indikator3) == LOW) and nilai > 55) {
digitalWrite(indikator3, HIGH);
digitalWrite(indikator2, HIGH);
digitalWrite(indikator1, HIGH);
lcd_aktuator.print("Peningkatan Suhu");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Sedikit");
lcd_aktuator.setCursor(9, 2);
lcd_aktuator.print("Berbahaya");
lcd_aktuator.setCursor(1, 3);
lcd_aktuator.print("Lampu 50% Nyala");
led = "indikator3";
sendTemperatureStatusToThingsboard("Sedikit Berbahaya");
} if ((digitalRead(indikator2) == LOW) and nilai > 30) {
digitalWrite(indikator2, HIGH);
digitalWrite(indikator1, HIGH);
lcd_aktuator.print("Suhu");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Normal");
lcd_aktuator.setCursor(1, 2);
lcd_aktuator.print("Lampu 25% Nyala");
led = "indikator2";
sendTemperatureStatusToThingsboard("Normal");
} if ((digitalRead(indikator1) == LOW) and nilai > 0) {
digitalWrite(indikator1, HIGH);
lcd_aktuator.print("Suhu");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Status: Aman");
lcd_aktuator.setCursor(1, 2);
lcd_aktuator.print("Lampu 0% Nyala");
led = "indikator1";
sendTemperatureStatusToThingsboard("Aman");
} sendLedToThingsboard(led, "Nyala");
}
}
void matiin_lampu(int nilai) {
String led;
if ((digitalRead(indikator5) == HIGH) and (nilai >= 100)) {
digitalWrite(indikator5, LOW);
led = "indikator5";
} if ((digitalRead(indikator4) == HIGH) and (nilai > 80 and nilai < 100)) {
digitalWrite(indikator5, LOW);
digitalWrite(indikator4, LOW);
led = "indikator4";
} if ((digitalRead(indikator3) == HIGH) and (nilai > 55 and nilai <= 80)) {
digitalWrite(indikator5, LOW);
digitalWrite(indikator4, LOW);
digitalWrite(indikator3, LOW);
led = "indikator3";
} if ((digitalRead(indikator2) == HIGH) and (nilai > 30 and nilai <= 55)) {
digitalWrite(indikator5, LOW);
digitalWrite(indikator4, LOW);
digitalWrite(indikator3, LOW);
digitalWrite(indikator2, LOW);
led = "indikator2";
} if ((digitalRead(indikator1) == HIGH) and (nilai <= 30)) {
digitalWrite(indikator5, LOW);
digitalWrite(indikator4, LOW);
digitalWrite(indikator3, LOW);
digitalWrite(indikator2, LOW);
digitalWrite(indikator1, LOW);
led = "indikator1";
} sendLedToThingsboard(led, "Mati");
}
// Sensor Asap
float tarik_data_asap() {
smoke = analogRead(Sensor_Asap);
return smoke;
}
// Sensor Gas Bocor
float tarik_data_gas_bocor() {
gas = analogRead(Sensor_Gas_Bocor);
return gas;
}
// Sensor Suhu
float tarik_data_suhu() {
temperature = hitung_celcius(analogRead(Sensor_Suhu));
if (temperature >= 79) {temperature = temperature * 1.5;}
char tmp_char = char(223);
String tmp = String(temperature, 2);
tmp += "";
tmp += tmp_char;
tmp += "C";
lcd_sensor.setCursor(12, 3);
lcd_sensor.print(" ");
lcd_sensor.setCursor(12, 3);
lcd_sensor.print(tmp);
return temperature;
}
void danger() {
int i = 0;
while ((not(digitalRead(switch1))) and ((tarik_data_suhu() > 100) and (tarik_data_asap() >= 3693))) {
if (i >= 2) {
rule_based(0, smoke);
delay(1000);
rule_based(2, temperature);
delay(1000);
lcd_aktuator.init();
lcd_aktuator.setCursor(5, 1);
lcd_aktuator.print("DANGERRR!!");
lcd_aktuator.setCursor(5, 2);
lcd_aktuator.print("KEBAKARAN");
digitalWrite(indikator_danger, HIGH);
tone(buzzer, 520);
sendLedToThingsboard("Indikator Danger", "Nyala");
sendSmokeStatusToThingsboard("DANGERRR!!");
sendTemperatureStatusToThingsboard("DANGERRR!!");
sendBuzzerToThingsboard("Nyala");
delay(1000);
digitalWrite(indikator_danger, LOW);
noTone(buzzer);
delay(500);
}
if (i < 2) {
rule_based(0, smoke);
delay(1000);
rule_based(2, temperature);
delay(1500);
matiin_lampu(tarik_data_suhu());
i++;
}
rule_based(1, tarik_data_gas_bocor());
delay(500);
sendTelemetryToThingsboard(hitung_ppm(1, gas), hitung_ppm(0, smoke), temperature);
} i = 0;
while ((not(digitalRead(switch1))) and (tarik_data_suhu() > 100)) {
if (tarik_data_asap() >= 922) {danger();}
if (i >= 2) {
rule_based(2, temperature);
delay(1000);
lcd_aktuator.init();
lcd_aktuator.setCursor(5, 1);
lcd_aktuator.print("DANGERRR!!");
lcd_aktuator.setCursor(8, 2);
lcd_aktuator.print("API");
digitalWrite(indikator_danger, HIGH);
tone(buzzer, 520);
sendLedToThingsboard("Indikator Danger", "Nyala");
sendTemperatureStatusToThingsboard("DANGERRR!!");
sendBuzzerToThingsboard("Nyala");
delay(1000);
digitalWrite(indikator_danger, LOW);
noTone(buzzer);
delay(500);
}
if (i < 2) {
rule_based(2, temperature);
delay(1500);
matiin_lampu(tarik_data_suhu());
}
rule_based(1, tarik_data_gas_bocor());
delay(500);
rule_based(0, tarik_data_asap());
delay(500);
sendTelemetryToThingsboard(hitung_ppm(1, gas), hitung_ppm(0, smoke), temperature);
i++;
} i = 0;
while ((not(digitalRead(switch1))) and (tarik_data_asap() >= 3578)) {
rule_based(0, smoke);
delay(1000);
if (tarik_data_suhu() > 100) {danger();}
if (i >= 2) {
lcd_aktuator.init();
lcd_aktuator.setCursor(5, 1);
lcd_aktuator.print("DANGERRR!!");
lcd_aktuator.setCursor(5, 2);
lcd_aktuator.print("ASAP TEBAL");
digitalWrite(indikator_danger, HIGH);
tone(buzzer, 520);
sendLedToThingsboard("Indikator Danger", "Nyala");
sendSmokeStatusToThingsboard("DANGERRR!!");
sendBuzzerToThingsboard("Nyala");
delay(1000);
digitalWrite(indikator_danger, LOW);
noTone(buzzer);
delay(500);
}
rule_based(1, tarik_data_gas_bocor());
delay(500);
rule_based(2, tarik_data_suhu());
delay(1500);
matiin_lampu(tarik_data_suhu());
delay(500);
sendTelemetryToThingsboard(hitung_ppm(1, gas), hitung_ppm(0, smoke), temperature);
i++;
} i = 0;
while ((not(digitalRead(switch1))) and (tarik_data_gas_bocor() >= 3289)) {
rule_based(1, gas);
delay(1000);
if (i >= 2) {
lcd_aktuator.init();
lcd_aktuator.setCursor(5, 1);
lcd_aktuator.print("DANGERRR!!");
lcd_aktuator.setCursor(5, 2);
lcd_aktuator.print("GAS BOCOR");
digitalWrite(indikator_danger, HIGH);
tone(buzzer, 520);
sendLedToThingsboard("Indikator Danger", "Nyala");
sendGasStatusToThingsboard("DANGERRR!!");
sendBuzzerToThingsboard("Nyala");
delay(1000);
digitalWrite(indikator_danger, LOW);
noTone(buzzer);
delay(500);
}
rule_based(0, tarik_data_asap());
delay(500);
rule_based(2, tarik_data_suhu());
delay(1500);
matiin_lampu(tarik_data_suhu());
delay(500);
sendTelemetryToThingsboard(hitung_ppm(1, gas), hitung_ppm(0, smoke), temperature);
i++;
}
sendLedToThingsboard("Indikator Danger", "Mati");
sendTemperatureStatusToThingsboard("");
sendSmokeStatusToThingsboard("");
sendGasStatusToThingsboard("");
sendBuzzerToThingsboard("Mati");
}
void setup() {// put your setup code here, to run once
Serial.begin(SERIAL_DEBUG_BAUD);
analogReadResolution(12);
// on/off
pinMode(switch1, INPUT_PULLUP);
// Lampu LED
pinMode(indikator1, OUTPUT); // 0% green
pinMode(indikator2, OUTPUT); // 25% yellow
pinMode(indikator3, OUTPUT); // 50% yellow
pinMode(indikator4, OUTPUT); // 75% red
pinMode(indikator5, OUTPUT); // 100% red
pinMode(indikator_danger, OUTPUT); // DANGER!
// Buzzer
pinMode(buzzer, OUTPUT);
delay(200);
}
void loop() {// put your main code here, to run repeatedly
// Sensor Asap
if (not(digitalRead(switch1))) {
if (itr == 0) {
// LCD
lcd_sensor.init();
lcd_aktuator.init();
lcd_sensor.backlight();
lcd_aktuator.backlight();
// loading screen
lcd_sensor.createChar(1, pacman);
lcd_sensor.createChar(2, dot);
lcd_aktuator.createChar(1, pacman);
lcd_aktuator.createChar(2, dot);
lcd_sensor.init();
lcd_aktuator.init();
lcd_sensor.setCursor(5, 0);
lcd_aktuator.setCursor(5, 0);
lcd_sensor.print("Kelompok 8");
lcd_aktuator.print("Kelompok 8");
lcd_sensor.setCursor(0, 2);
lcd_aktuator.setCursor(0, 2);
lcd_sensor.print("Menyalakan Perangkat");
lcd_aktuator.print("Menyalakan Perangkat");
for (int i = 3; i < 16; i++) {
lcd_sensor.setCursor(i, 3);
lcd_aktuator.setCursor(i, 3);
lcd_sensor.print("\1");
lcd_aktuator.print("\1");
for (int j = i + 1; j < 16; j++) {
lcd_sensor.setCursor(j, 3);
lcd_aktuator.setCursor(j, 3);
lcd_sensor.print("\2");
lcd_aktuator.print("\2");
}
lcd_sensor.createChar(1, pacman);
lcd_aktuator.createChar(1, pacman);
delay(200);
lcd_sensor.createChar(1, pacmanOpen);
lcd_aktuator.createChar(1, pacmanOpen);
delay(200);
lcd_sensor.setCursor(i, 3);
lcd_aktuator.setCursor(i, 3);
lcd_sensor.print(" ");
lcd_aktuator.print(" ");
}
sendQuoteToThingsboard();
itr++;
}
if (itr == 1) {
// THINGSBOARD ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
InitWiFi();
delay(1000);
lcd_sensor.init();
lcd_aktuator.init();
if (WiFi.status() == WL_CONNECTED) {
lcd_sensor.setCursor(1, 1);
lcd_sensor.print("Connected to Wifi!");
lcd_aktuator.setCursor(1, 1);
lcd_aktuator.print("Connected to Wifi!");
} else {
lcd_sensor.setCursor(7, 1);
lcd_sensor.print("Cannot");
lcd_aktuator.setCursor(7, 1);
lcd_aktuator.print("Cannot");
lcd_sensor.setCursor(1, 2);
lcd_sensor.print("Connected to Wifi!");
lcd_aktuator.setCursor(1, 2);
lcd_aktuator.print("Connected to Wifi!");
}
delay(1000);
setUpThingsboard();
lcd_sensor.init();
lcd_aktuator.init();
if (tb.connected()) {
lcd_sensor.setCursor(4, 1);
lcd_aktuator.setCursor(4, 1);
lcd_sensor.print("Connected to");
lcd_aktuator.print("Connected to");
lcd_sensor.setCursor(4, 2);
lcd_aktuator.setCursor(4, 2);
lcd_sensor.print("Thingsboard!");
lcd_aktuator.print("Thingsboard!");
} else {
lcd_sensor.setCursor(7, 1);
lcd_sensor.print("Cannot");
lcd_aktuator.setCursor(7, 1);
lcd_aktuator.print("Cannot");
lcd_sensor.setCursor(4, 2);
lcd_aktuator.setCursor(4, 2);
lcd_sensor.print("Connected to");
lcd_aktuator.print("Connected to");
lcd_sensor.setCursor(4, 3);
lcd_aktuator.setCursor(4, 3);
lcd_sensor.print("Thingsboard!");
lcd_aktuator.print("Thingsboard!");
}
delay(1000);
// ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
// LCD
lcd_sensor.init();
lcd_aktuator.init();
delay(1000);
lcd_sensor.setCursor(1, 0);
lcd_sensor.print("Kelompok 8:");
lcd_sensor.setCursor(1, 1);
lcd_sensor.print("Asap");
lcd_sensor.setCursor(1, 2);
lcd_sensor.print("Gas Bocor");
lcd_sensor.setCursor(1, 3);
lcd_sensor.print("Suhu");
for (int i = 1; i < 4; i++) {
lcd_sensor.setCursor(11, i);
lcd_sensor.print(":");
}
delay(200);
// Note: In real hardware, the sensor needs a pre-heating time of about 20-30 seconds before taking readings. The simulator provides readings immediately.
}
// Rule Based & Aktivasi Aktuator
rule_based(1, tarik_data_gas_bocor());
delay(1000);
rule_based(0, tarik_data_asap());
delay(1000);
rule_based(2, tarik_data_suhu());
// THINGSBOARD ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
sendTelemetryToThingsboard(hitung_ppm(1, gas), hitung_ppm(0, smoke), temperature);
tb.loop();
// ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
danger();
delay(1000);
matiin_lampu(tarik_data_suhu());
delay(1000);
// THINGSBOARD ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
if ((itr % 5 == 0) and ((WiFi.status() != WL_CONNECTED) or (!tb.connected()))) {
itr = 0;
}
// ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
itr++;
}
if (digitalRead(switch1)) {
if (itr > 0) {
// tambahkan goodbye di sini & matiin status "online" di thingsboard
digitalWrite(indikator5, LOW);
digitalWrite(indikator4, LOW);
digitalWrite(indikator3, LOW);
digitalWrite(indikator2, LOW);
digitalWrite(indikator1, LOW);
tb.sendTelemetryData("status_perangkat", "Offline");
tb.sendTelemetryData("status_led", "");
tb.sendTelemetryData("temperature", 0);
tb.sendTelemetryData("gas", 0);
tb.sendTelemetryData("smoke", 0);
tb.sendTelemetryData("fan_speed", 0);
tb.sendTelemetryData("led", "");
tb.sendTelemetryData("exhaust_fan", "");
tb.sendTelemetryData("buzzer", "");
lcd_sensor.init();
lcd_aktuator.init();
lcd_sensor.setCursor(1, 1);
lcd_aktuator.setCursor(1, 1);
lcd_sensor.print("Memutuskan Koneksi");
lcd_aktuator.print("Memutuskan Koneksi");
tb.disconnect();
Serial.println("Koneksi MQTT terputus.");
WiFi.disconnect();
Serial.println("Koneksi WiFi terputus.");
delay(1000);
lcd_sensor.init();
lcd_aktuator.init();
lcd_sensor.noBacklight();
lcd_aktuator.noBacklight();
itr = 0;
}
}
}Asap
Gas Bocor
Suhu
Sensor
Aktuator
Exhaust Fan
Buzz
Loading
esp32-devkit-c-v4
esp32-devkit-c-v4