LAPORAN AKHIR M4



 LAPORAN AKHIR DEMO PROJECT

Sistem Monitoring dan Deteksi Kebocoran LPG pada Dapur Hunian Sementara

a. Rangkaian Simulasi dan Prinsip Kerja [kembali]

Gambar 1. Rangkaian Simulasi Proteus

Prinsip Kerja:

    Rangkaian ini berfungsi sebagai Sistem Keselamatan Pintar untuk Dapur Hunian Sementara (Huntara) yang dikendalikan oleh mikrokontroler STM32 melalui pembacaan tiga buah sensor utama, yaitu sensor gas MQ-6, sensor api (flame sensor), dan sensor getaran (vibration sensor). Pada saat sistem pertama kali dinyalakan, mikrokontroler akan melakukan inisialisasi terhadap seluruh pin Input/Output (I/O) serta mengaktifkan layar LCD 16x2 dengan menampilkan pesan pembuka (splash screen). Selain itu, sistem akan melakukan proses pemanasan awal (warm-up) pada Analog-to-Digital Converter (ADC) dengan mengambil sampel nilai dari sensor MQ-6 sebanyak sepuluh kali berturut-turut untuk mengisi data awal pada algoritma penyaringan.

    Setelah fase inisialisasi selesai, mikrokontroler masuk ke dalam siklus pengulangan utama (main loop) yang berjalan secara terus-menerus untuk memantau kondisi lingkungan dapur. Pemantauan nilai analog dari sensor MQ-6 yang terhubung pada pin PA0 diproses menggunakan metode Moving Average Filter (Penyaringan Rata-rata Bergerak) berukuran sepuluh data guna meredam fluktuasi atau derau (noise) sinyal analog, sehingga data pembacaan konsentrasi gas menjadi lebih stabil dan akurat. Secara bersamaan, mikrokontroler memantau pin digital PA1 untuk mendeteksi keberadaan api dari flame sensor yang bekerja secara aktif rendah (active low), serta memantau pin digital PA2 untuk mendeteksi getaran struktur bangunan dari vibration sensor yang bekerja secara aktif tinggi (active high).

    Sistem kemudian melakukan evaluasi logika keselamatan dengan mencocokkan hasil pembacaan ketiga sensor tersebut terhadap nilai ambang batas (threshold) yang telah ditentukan. Jika hasil filter sensor MQ-6 melampaui nilai ambang batas 2457 (atau setara dengan tegangan sekitar 3V pada ADC 12-bit) dan flame sensor mendeteksi adanya api, maka sistem mengidentifikasi kondisi tersebut sebagai Bahaya Kebakaran. Sebagai respon darurat, mikrokontroler akan langsung menyalakan LED Merah, LED Kuning, Kipas angin untuk mengusir gas, serta membunyikan Buzzer sebagai alarm peringatan, diikuti dengan tampilan visual pada layar LCD berupa teks peringatan bahaya kebakaran.

    Namun, jika sensor MQ-6 mendeteksi kebocoran gas tetapi tidak ditemukan adanya indikasi api, sistem mengategorikannya sebagai kondisi Gas Bocor Tanpa Api. Pada kondisi ini, aktuator yang aktif meliputi LED Merah, Kipas, dan Buzzer, sementara layar LCD akan menginformasikan terjadinya kebocoran gas tanpa api. Sebaliknya, apabila hanya flame sensor yang mendeteksi api sedangkan kadar gas dinyatakan aman, sistem menganggap hal tersebut sebagai aktivitas Kompor Menyala Normal sehingga hanya menyalakan LED Kuning dan Buzzer sebagai penanda visual serta audio bahwa kompor sedang digunakan.

    Di samping deteksi gas dan api, sistem ini juga tanggap terhadap ancaman struktural melalui sensor getaran. Ketika terjadi guncangan hebat atau gempa yang memicu vibration sensor bernilai logika tinggi, mikrokontroler akan langsung menyalakan LED Biru dan membunyikan Buzzer secara responsif, lalu menampilkan pesan Gempa/Getaran! Evakuasi Segera pada layar LCD guna memperingatkan penghuni agar menyelamatkan diri. Terakhir, apabila seluruh parameter lingkungan berada di bawah ambang batas bahaya, sistem akan tetap berada pada Kondisi Standby / Aman, di mana seluruh lampu LED, kipas, dan buzzer dimatikan, sementara layar LCD secara berkala menampilkan status keamanan beserta nilai ADC real-time dari sensor gas MQ-6.

Rangkaian Prototype:

Gambar 2. Rangkaian Prototype

b. Flowchart dan Listing Program [kembali]

Flowchart

Gambar 3. Flowchart
Listing Program Proteus:
program untuk proteus

#include "main.h"
#include <stdio.h>

/* =========================================================
 * DEFINES
 * ========================================================= */
#define FILTER_SIZE 10

/* MQ-6 Gas Sensor Threshold (Tetap ADC Analog) */
#define THRESH_MQ6_GAS_BOCOR       2457U   /* ~3V pada ADC 12-bit Vref 3.3V   */

/* =========================================================
 * PIN SENSOR
 * ========================================================= */
#define PIN_FLAME       GPIO_PIN_1   /* PA1 -> Flame DO (AKTIF LOW)       */
#define PIN_VIBRATION   GPIO_PIN_2   /* PA2 -> Vibration DO (AKTIF HIGH)  */

/* =========================================================
 * PIN AKTUATOR (PORT B)
 * ========================================================= */
#define PIN_LED_KUNING  GPIO_PIN_0   /* PB0  -> LED Kuning (api/kompor)   */
#define PIN_LED_MERAH   GPIO_PIN_1   /* PB1  -> LED Merah  (gas bocor)    */
#define PIN_LED_BIRU    GPIO_PIN_2   /* PB2  -> LED Biru   (getaran)      */
#define PIN_KIPAS       GPIO_PIN_9   /* PB9  -> Kipas                     */
#define PIN_BUZZER      GPIO_PIN_11  /* PB11 -> Buzzer                    */

/* =========================================================
 * LCD 4-BIT (PORT B)
 * ========================================================= */
#define LCD_RS_GPIO_Port  GPIOB
#define LCD_RS_Pin        GPIO_PIN_5
#define LCD_E_GPIO_Port   GPIOB
#define LCD_E_Pin         GPIO_PIN_4
#define LCD_D4_GPIO_Port  GPIOB
#define LCD_D4_Pin        GPIO_PIN_12
#define LCD_D5_GPIO_Port  GPIOB
#define LCD_D5_Pin        GPIO_PIN_13
#define LCD_D6_GPIO_Port  GPIOB
#define LCD_D6_Pin        GPIO_PIN_14
#define LCD_D7_GPIO_Port  GPIOB
#define LCD_D7_Pin        GPIO_PIN_15

/* =========================================================
 * VARIABLES
 * ========================================================= */
ADC_HandleTypeDef hadc1;

uint16_t adc_buffer_mq6 = 0;
uint32_t filtered_MQ6   = 0;
uint16_t mq6_buf[FILTER_SIZE];
uint8_t  idx_mq6        = 0;

/* =========================================================
 * FUNCTION PROTOTYPES
 * ========================================================= */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
uint32_t Filter_MQ6(uint16_t val);

void LCD_EnablePulse(void);
void LCD_SendNibble(uint8_t nibble);
void LCD_SendCommand(uint8_t cmd);
void LCD_SendData(uint8_t data);
void LCD_Init(void);
void LCD_SendString(char *str);
void LCD_SetCursor(uint8_t row, uint8_t col);

/* =========================================================
 * FILTER MOVING AVERAGE FOR MQ-6
 * ========================================================= */
uint32_t Filter_MQ6(uint16_t val) {
    mq6_buf[idx_mq6++] = val;
    if (idx_mq6 >= FILTER_SIZE) idx_mq6 = 0;
    uint32_t sum = 0;
    for (int i = 0; i < FILTER_SIZE; i++) sum += mq6_buf[i];
    return (sum / FILTER_SIZE);
}

/* =========================================================
 * DRIVER LCD PARALLEL 4-BIT
 * ========================================================= */
void LCD_EnablePulse(void) {
    HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_SET);   HAL_Delay(1);
    HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_RESET); HAL_Delay(1);
}

void LCD_SendNibble(uint8_t nibble) {
    HAL_GPIO_WritePin(LCD_D4_GPIO_Port, LCD_D4_Pin, (nibble & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LCD_D5_GPIO_Port, LCD_D5_Pin, (nibble & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LCD_D6_GPIO_Port, LCD_D6_Pin, (nibble & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LCD_D7_GPIO_Port, LCD_D7_Pin, (nibble & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    LCD_EnablePulse();
}

void LCD_SendCommand(uint8_t cmd) {
    HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_Pin, GPIO_PIN_RESET);
    LCD_SendNibble(cmd >> 4);
    LCD_SendNibble(cmd & 0x0F);
    HAL_Delay(2);
}

void LCD_SendData(uint8_t data) {
    HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_Pin, GPIO_PIN_SET);
    LCD_SendNibble(data >> 4);
    LCD_SendNibble(data & 0x0F);
}

void LCD_Init(void) {
    HAL_Delay(50);
    HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LCD_E_GPIO_Port,  LCD_E_Pin,  GPIO_PIN_RESET);
    LCD_SendNibble(0x03); HAL_Delay(5);
    LCD_SendNibble(0x03); HAL_Delay(1);
    LCD_SendNibble(0x03); HAL_Delay(1);
    LCD_SendNibble(0x02); HAL_Delay(10);
    LCD_SendCommand(0x28);
    LCD_SendCommand(0x0C);
    LCD_SendCommand(0x06);
    LCD_SendCommand(0x01);
    HAL_Delay(2);
}

void LCD_SendString(char *str) {
    while (*str) LCD_SendData((uint8_t)*str++);
}

void LCD_SetCursor(uint8_t row, uint8_t col) {
    LCD_SendCommand((row == 0) ? (0x80 + col) : (0xC0 + col));
}

/* =========================================================
 * MAIN
 * ========================================================= */
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_ADC1_Init();

    /* Pastikan semua aktuator OFF saat start */
    HAL_GPIO_WritePin(GPIOB,
        PIN_LED_KUNING | PIN_LED_MERAH | PIN_LED_BIRU |
        PIN_KIPAS | PIN_BUZZER,
        GPIO_PIN_RESET);

    /* Inisialisasi buffer filter MQ-6 */
    for (int i = 0; i < FILTER_SIZE; i++) {
        mq6_buf[i] = 0;
    }

    /* Tampilan splash screen */
    LCD_Init();
    LCD_SetCursor(0, 0); LCD_SendString("  HUNTARA SMART ");
    LCD_SetCursor(1, 0); LCD_SendString("  SAFETY SYSTEM ");
    HAL_Delay(2000);
    LCD_SendCommand(0x01); HAL_Delay(2);

    /* Warm-up ADC untuk MQ-6 saja (CH0) */
    for (int w = 0; w < FILTER_SIZE; w++) {
        HAL_ADC_Start(&hadc1);
        if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) {
            adc_buffer_mq6 = (uint16_t)HAL_ADC_GetValue(&hadc1);
        }
        HAL_ADC_Stop(&hadc1);
        Filter_MQ6(adc_buffer_mq6);
        HAL_Delay(10);
    }

    /* =========================================================
     * MAIN LOOP
     * ========================================================= */
    while (1)
    {
        /* --------------------------------------------------
         * LANGKAH 1: Baca ADC -- MQ-6 (Hanya Single Channel CH0)
         * -------------------------------------------------- */
        HAL_ADC_Start(&hadc1);
        if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) {
            adc_buffer_mq6 = (uint16_t)HAL_ADC_GetValue(&hadc1);
        }
        HAL_ADC_Stop(&hadc1);

        /* --------------------------------------------------
         * LANGKAH 2: Filter Moving Average MQ-6
         * -------------------------------------------------- */
        filtered_MQ6 = Filter_MQ6(adc_buffer_mq6);

        /* --------------------------------------------------
         * LANGKAH 3: Baca Flame Sensor (PA1 - AKTIF LOW)
         * -------------------------------------------------- */
        GPIO_PinState flame_pin = HAL_GPIO_ReadPin(GPIOA, PIN_FLAME);
        uint8_t flame_detected  = (flame_pin == GPIO_PIN_RESET) ? 1U : 0U;

        /* --------------------------------------------------
         * LANGKAH 4: Baca Vibration Sensor (PA2 - AKTIF HIGH)
         * -------------------------------------------------- */
        GPIO_PinState vib_pin      = HAL_GPIO_ReadPin(GPIOA, PIN_VIBRATION);
        uint8_t vibration_detected = (vib_pin == GPIO_PIN_SET) ? 1U : 0U;

        /* --------------------------------------------------
         * LANGKAH 5: Evaluasi Kondisi Keselamatan
         * -------------------------------------------------- */
        uint8_t mq6_aktif     = (filtered_MQ6 >= THRESH_MQ6_GAS_BOCOR) ? 1U : 0U;
        uint8_t kebakaran     = (mq6_aktif &&  flame_detected) ? 1U : 0U;
        uint8_t gas_tanpa_api = (mq6_aktif && !flame_detected) ? 1U : 0U;
        uint8_t kompor_normal = (!mq6_aktif &&  flame_detected) ? 1U : 0U;

        /* --------------------------------------------------
         * LANGKAH 6: Kontrol Aktuator (LED, Kipas, Buzzer)
         * -------------------------------------------------- */
        // LED Merah & Kipas aktif jika ada kebocoran gas
        HAL_GPIO_WritePin(GPIOB, PIN_LED_MERAH, (kebakaran || gas_tanpa_api) ? GPIO_PIN_SET : GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOB, PIN_KIPAS,     (kebakaran || gas_tanpa_api) ? GPIO_PIN_SET : GPIO_PIN_RESET);

        // LED Kuning aktif jika ada api terdeteksi
        HAL_GPIO_WritePin(GPIOB, PIN_LED_KUNING, (kebakaran || kompor_normal) ? GPIO_PIN_SET : GPIO_PIN_RESET);

        // LED Biru aktif jika terdeteksi getaran struktur bangunan
        HAL_GPIO_WritePin(GPIOB, PIN_LED_BIRU, (vibration_detected) ? GPIO_PIN_SET : GPIO_PIN_RESET);

        // BUZZER aktif jika: Kebakaran, Gas Bocor, Kompor Normal, atau Terjadi Getaran (Gempa)
        HAL_GPIO_WritePin(GPIOB, PIN_BUZZER,
            (kebakaran || gas_tanpa_api || kompor_normal || vibration_detected) ? GPIO_PIN_SET : GPIO_PIN_RESET);

        /* --------------------------------------------------
         * LANGKAH 7: Tampilan LCD (Prioritas Atas -> Bawah)
         * -------------------------------------------------- */
        LCD_SendCommand(0x01); HAL_Delay(2);

        if (kebakaran) {
            LCD_SetCursor(0, 0); LCD_SendString("GAS BOCOR + API ");
            LCD_SetCursor(1, 0); LCD_SendString("BAHAYA KEBAKARAN");
        }
        else if (gas_tanpa_api) {
            LCD_SetCursor(0, 0); LCD_SendString("GAS BOCOR!      ");
            LCD_SetCursor(1, 0); LCD_SendString("TANPA API       ");
        }
        else if (kompor_normal) {
            LCD_SetCursor(0, 0); LCD_SendString("Kompor Menyala- ");
            LCD_SetCursor(1, 0); LCD_SendString("kan Api         ");
        }
        else if (vibration_detected) {
            LCD_SetCursor(0, 0); LCD_SendString("GEMPA/GETARAN!  ");
            LCD_SetCursor(1, 0); LCD_SendString("EVAKUASI SEGERA ");
        }
        else {
            // Kondisi Standby / Aman
            char lcd_msg[17];
            LCD_SetCursor(0, 0); LCD_SendString("SISTEM AMAN     ");
            sprintf(lcd_msg, "MQ6 ADC:%-4lu   ", filtered_MQ6);
            LCD_SetCursor(1, 0); LCD_SendString(lcd_msg);
        }

        HAL_Delay(250);
    }
}

/* =========================================================
 * SYSTEM CLOCK CONFIG
 * ========================================================= */
void SystemClock_Config(void) {
    RCC_OscInitTypeDef       RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef       RCC_ClkInitStruct = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInit     = {0};

    RCC_OscInitStruct.OscillatorType      = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState            = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState        = RCC_PLL_NONE;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) Error_Handler();

    RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                     | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_HSI;
    RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) Error_Handler();

    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInit.AdcClockSelection    = RCC_ADCPCLK2_DIV2;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) Error_Handler();
}

/* =========================================================
 * ADC1 INIT (Disederhanakan untuk Single Channel MQ-6 di PA0)
 * ========================================================= */
static void MX_ADC1_Init(void) {
    ADC_ChannelConfTypeDef sConfig = {0};

    hadc1.Instance                   = ADC1;
    hadc1.Init.ScanConvMode          = ADC_SCAN_DISABLE; // Disable scan karena hanya 1 channel
    hadc1.Init.ContinuousConvMode    = DISABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv      = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign             = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion       = 1;
    if (HAL_ADC_Init(&hadc1) != HAL_OK) Error_Handler();

    sConfig.Channel      = ADC_CHANNEL_0; // PA0 -> MQ-6 Gas
    sConfig.Rank         = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
}

/* =========================================================
 * GPIO INIT
 * ========================================================= */
static void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();

    HAL_GPIO_WritePin(GPIOB,
        PIN_LED_KUNING | PIN_LED_MERAH | PIN_LED_BIRU |
        PIN_KIPAS | PIN_BUZZER |
        LCD_RS_Pin | LCD_E_Pin |
        LCD_D4_Pin | LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin,
        GPIO_PIN_RESET);

    /* Output Konfigurasi: LED, Kipas, Buzzer */
    GPIO_InitStruct.Pin   = PIN_LED_KUNING | PIN_LED_MERAH | PIN_LED_BIRU |
                            PIN_KIPAS | PIN_BUZZER;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Output Konfigurasi: LCD */
    GPIO_InitStruct.Pin   = LCD_RS_Pin | LCD_E_Pin |
                            LCD_D4_Pin | LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Input Konfigurasi: PA1 -> Flame Sensor (AKTIF LOW) */
    GPIO_InitStruct.Pin   = PIN_FLAME;
    GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull  = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* Input Konfigurasi: PA2 -> Vibration Sensor (AKTIF HIGH) */
    GPIO_InitStruct.Pin   = PIN_VIBRATION;
    GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull  = GPIO_PULLDOWN; // Dipasang Pulldown agar default berlogika LOW jika diam
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

/* =========================================================
 * ERROR HANDLER
 * ========================================================= */
void Error_Handler(void) {
    __disable_irq();
    while (1) {}
}


Listing Program untuk Prototype:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body (Huntara Smart Safety System - LCD I2C)
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include <stdio.h>

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define FILTER_SIZE 10

/* Alamat I2C PCF8574 (Umumnya 0x27 << 1 = 0x4E atau 0x3F << 1 = 0x7E) */
#define LCD_I2C_SLAVE_ADDR      0x4EU

/* Bitmask Kontrol PCF8574 ke LCD */
#define LCD_PIN_RS              (1U << 0)
#define LCD_PIN_RW              (1U << 1)
#define LCD_PIN_EN              (1U << 2)
#define LCD_PIN_BACKLIGHT       (1U << 3)

/* MQ-6 Gas Sensor */
#define THRESH_MQ6_GAS_BOCOR    2457U   /* ~3V pada ADC 12-bit Vref 3.3V */

/* SN-209 Pressure Sensor (0-50 kPa, Vout 0-5V, ADC 12-bit Vref 3.3V) */
#define PRESS_MIN_VALID         1241U   /* ~10 kPa -- batas bawah valid */
#define PRESS_RATE_LEAK_THRESH  246U    /* >=6%/s  -> LED Biru + Buzzer ON */
#define PRESS_RATE_WARN_THRESH  82U     /* >=2%/s  -> LED Biru saja, Buzzer OFF */
#define PRESS_SAMPLE_INTERVAL_MS 1000U

/* Landmark level isi tabung */
#define PRESS_ADC_FULL          3723U   /* ~30 kPa -- tabung penuh */
#define PRESS_ADC_HALF          2482U   /* ~20 kPa -- setengah */
#define PRESS_ADC_LOW           1241U   /* ~10 kPa -- hampir habis */

/* PIN SENSOR & BUTTON */
#define PIN_FLAME               GPIO_PIN_1   /* PA1 -> Flame DO AKTIF LOW */
#define PIN_BUTTON              GPIO_PIN_3   /* PA3 -> Push Button Fan (AKTIF LOW) */

/* PIN AKTUATOR */
#define PIN_LED_KUNING          GPIO_PIN_0   /* PB0  -> LED Kuning -> AKTIF LOW */
#define PIN_LED_MERAH           GPIO_PIN_1   /* PB1  -> LED Merah  -> AKTIF LOW */
#define PIN_LED_BIRU            GPIO_PIN_2   /* PB2  -> LED Biru   -> AKTIF LOW */
#define PIN_KIPAS               GPIO_PIN_9   /* PB9  -> Relay Exhaust Fan -> AKTIF HIGH (NO) */
#define PIN_BUZZER              GPIO_PIN_11  /* PB11 -> Buzzer -> AKTIF LOW */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
I2C_HandleTypeDef hi2c1;

/* USER CODE BEGIN PV */
uint16_t adc_buffer[2];
uint32_t filtered_MQ6   = 0;
uint32_t filtered_Press = 0;

uint16_t mq6_buf[FILTER_SIZE];
uint16_t press_buf[FILTER_SIZE];
uint8_t  idx_mq6   = 0;
uint8_t  idx_press = 0;

uint32_t press_snapshot    = 0;
int32_t  press_delta       = 0;
uint8_t  press_rate_pct    = 0;
uint32_t last_sample_tick  = 0;
uint8_t  press_rate_state  = 0;   /* 0=normal, 1=WARN, 2=LEAK */

uint16_t press_kpa_x10      = 0;  /* nilai kPa x10 real-time */
uint16_t press_snap_kpa_x10 = 0;  /* snapshot kPa x10 */
uint8_t  press_snap_taken   = 0;  /* flag snapshot */

/* Variabel Pendukung Toggle Button & Debounce manual */
uint8_t  fan_manual_status  = 0;  /* 0 = MATI, 1 = NYALA */
uint8_t  last_button_state  = 1;  /* Standby HIGH */
uint32_t last_debounce_time = 0;

static const uint32_t adc_channels[2] = {
    ADC_CHANNEL_0,  /* PA0 -> MQ-6  */
    ADC_CHANNEL_2   /* PA2 -> SN-209 */
};
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
static void MX_I2C1_Init(void);
/* USER CODE BEGIN PFP */
uint32_t Filter_MQ6(uint16_t val);
uint32_t Filter_Press(uint16_t val);
uint16_t ADC_to_kPa_x10(uint32_t adc_val);
void     Update_PressRate(void);

/* Fungsi LCD I2C */
void Lcd_I2c_Write(uint8_t data, uint8_t flags);
void Lcd_I2c_Cmd(uint8_t cmd);
void Lcd_I2c_Data(uint8_t data);
void Lcd_I2c_Init(void);
void Lcd_I2c_SendString(char *str);
void Lcd_I2c_SetCursor(uint8_t row, uint8_t col);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* =========================================================
 * FILTER MOVING AVERAGE & KONVERSI
 * ========================================================= */
uint32_t Filter_MQ6(uint16_t val) {
    mq6_buf[idx_mq6++] = val;
    if (idx_mq6 >= FILTER_SIZE) idx_mq6 = 0;
    uint32_t sum = 0;
    for (int i = 0; i < FILTER_SIZE; i++) sum += mq6_buf[i];
    return (sum / FILTER_SIZE);
}

uint32_t Filter_Press(uint16_t val) {
    press_buf[idx_press++] = val;
    if (idx_press >= FILTER_SIZE) idx_press = 0;
    uint32_t sum = 0;
    for (int i = 0; i < FILTER_SIZE; i++) sum += press_buf[i];
    return (sum / FILTER_SIZE);
}

uint16_t ADC_to_kPa_x10(uint32_t adc_val) {
    return (uint16_t)((adc_val * 330UL) / 4095UL);
}

void Update_PressRate(void) {
    uint32_t now = HAL_GetTick();
    if ((now - last_sample_tick) < PRESS_SAMPLE_INTERVAL_MS) return;
    last_sample_tick = now;

    press_kpa_x10 = ADC_to_kPa_x10(filtered_Press);

    if (filtered_Press < PRESS_MIN_VALID) {
        press_delta       = 0;
        press_rate_pct    = 0;
        press_rate_state  = 0;
        press_snapshot    = filtered_Press;
        press_snap_taken  = 0;
        return;
    }

    if (!press_snap_taken) {
        press_snapshot      = filtered_Press;
        press_snap_kpa_x10  = press_kpa_x10;
        press_snap_taken    = 1;
        press_delta         = 0;
        press_rate_pct      = 0;
        press_rate_state    = 0;
        return;
    }

    press_delta = (int32_t)press_snapshot - (int32_t)filtered_Press;
    if (press_delta < 0) press_delta = 0;

    press_rate_pct = (uint8_t)((press_delta * 100UL) / 4095UL);

    if      ((uint32_t)press_delta >= PRESS_RATE_LEAK_THRESH) press_rate_state = 2;
    else if ((uint32_t)press_delta >= PRESS_RATE_WARN_THRESH) press_rate_state = 1;
    else                                                      press_rate_state = 0;

    press_snapshot = filtered_Press;
}

/* =========================================================
 * DRIVER LCD I2C (PCF8574)
 * ========================================================= */
void Lcd_I2c_Write(uint8_t data, uint8_t flags) {
    uint8_t up = (data & 0xF0) | flags | LCD_PIN_BACKLIGHT;
    uint8_t lo = ((data << 4) & 0xF0) | flags | LCD_PIN_BACKLIGHT;

    uint8_t data_arr[4] = {
        up | LCD_PIN_EN,
        up & ~LCD_PIN_EN,
        lo | LCD_PIN_EN,
        lo & ~LCD_PIN_EN
    };
    HAL_I2C_Master_Transmit(&hi2c1, LCD_I2C_SLAVE_ADDR, data_arr, 4, 100);
}

void Lcd_I2c_Cmd(uint8_t cmd) {
    Lcd_I2c_Write(cmd, 0);
}

void Lcd_I2c_Data(uint8_t data) {
    Lcd_I2c_Write(data, LCD_PIN_RS);
}

void Lcd_I2c_Init(void) {
    HAL_Delay(50);
    Lcd_I2c_Cmd(0x03 << 4); HAL_Delay(5);
    Lcd_I2c_Cmd(0x03 << 4); HAL_Delay(1);
    Lcd_I2c_Cmd(0x03 << 4); HAL_Delay(1);
    Lcd_I2c_Cmd(0x02 << 4); HAL_Delay(10);

    Lcd_I2c_Cmd(0x28); /* 4-bit mode, 2 baris, 5x8 font */
    Lcd_I2c_Cmd(0x0C); /* Display ON, Cursor OFF */
    Lcd_I2c_Cmd(0x06); /* Entry mode increment */
    Lcd_I2c_Cmd(0x01); /* Clear Display */
    HAL_Delay(2);
}

void Lcd_I2c_SendString(char *str) {
    while (*str) Lcd_I2c_Data((uint8_t)*str++);
}

void Lcd_I2c_SetCursor(uint8_t row, uint8_t col) {
    Lcd_I2c_Cmd((row == 0) ? (0x80 + col) : (0xC0 + col));
}
/* USER CODE END 0 */

int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/
  HAL_Init();
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_I2C1_Init();

  /* USER CODE BEGIN 2 */
  /* Kondisi Awal Aktuator (Standby/Mati) */
  HAL_GPIO_WritePin(GPIOB, PIN_LED_KUNING | PIN_LED_MERAH | PIN_LED_BIRU | PIN_BUZZER, GPIO_PIN_SET);
  HAL_GPIO_WritePin(GPIOB, PIN_KIPAS, GPIO_PIN_RESET);

  for (int i = 0; i < FILTER_SIZE; i++) {
      mq6_buf[i]   = 0;
      press_buf[i] = 0;
  }

  /* Inisialisasi & Splash Screen LCD I2C */
  Lcd_I2c_Init();
  Lcd_I2c_SetCursor(0, 0); Lcd_I2c_SendString("  HUNTARA SMART ");
  Lcd_I2c_SetCursor(1, 0); Lcd_I2c_SendString("  SAFETY SYSTEM ");
  HAL_Delay(2000);
  Lcd_I2c_Cmd(0x01); HAL_Delay(2);

  /* WARM-UP ADC */
  ADC_ChannelConfTypeDef sConfigWU = {0};
  sConfigWU.Rank         = ADC_REGULAR_RANK_1;
  sConfigWU.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;

  for (int w = 0; w < FILTER_SIZE; w++) {
      for (int i = 0; i < 2; i++) {
          sConfigWU.Channel = adc_channels[i];
          HAL_ADC_ConfigChannel(&hadc1, &sConfigWU);
          HAL_ADC_Start(&hadc1);
          if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
              adc_buffer[i] = (uint16_t)HAL_ADC_GetValue(&hadc1);
          HAL_ADC_Stop(&hadc1);
      }
      Filter_MQ6(adc_buffer[0]);
      filtered_Press = Filter_Press(adc_buffer[1]);
      HAL_Delay(10);
  }

  press_kpa_x10      = ADC_to_kPa_x10(filtered_Press);
  press_snapshot     = filtered_Press;
  press_snap_taken   = 0;
  last_sample_tick   = HAL_GetTick();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* LANGKAH 1: Baca ADC */
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Rank         = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;

    for (int i = 0; i < 2; i++) {
        sConfig.Channel = adc_channels[i];
        if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
        HAL_Delay(1);
        HAL_ADC_Start(&hadc1);
        if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
            adc_buffer[i] = (uint16_t)HAL_ADC_GetValue(&hadc1);
        HAL_ADC_Stop(&hadc1);
    }

    /* LANGKAH 2: Filter Moving Average */
    filtered_MQ6   = Filter_MQ6(adc_buffer[0]);
    filtered_Press = Filter_Press(adc_buffer[1]);

    /* LANGKAH 3: Baca Flame & Debounce Button */
    GPIO_PinState flame_pin      = HAL_GPIO_ReadPin(GPIOA, PIN_FLAME);
    uint8_t       flame_detected = (flame_pin == GPIO_PIN_RESET) ? 1U : 0U;

    GPIO_PinState current_button_pin = HAL_GPIO_ReadPin(GPIOA, PIN_BUTTON);

    if (current_button_pin != last_button_state) {
        last_debounce_time = HAL_GetTick();
    }

    if ((HAL_GetTick() - last_debounce_time) > 50) {
        if (current_button_pin == GPIO_PIN_RESET && last_button_state == 1) {
            fan_manual_status = !fan_manual_status;
        }
    }

    if ((HAL_GetTick() - last_debounce_time) > 50) {
        last_button_state = current_button_pin;
    }

    /* LANGKAH 4: Update Rate-of-Change Tekanan */
    Update_PressRate();
    press_kpa_x10 = ADC_to_kPa_x10(filtered_Press);

    /* LANGKAH 5: Evaluasi Kondisi */
    uint8_t mq6_aktif     = (filtered_MQ6 >= THRESH_MQ6_GAS_BOCOR) ? 1U : 0U;
    uint8_t kebakaran     = (mq6_aktif &&  flame_detected) ? 1U : 0U;
    uint8_t gas_tanpa_api = (mq6_aktif && !flame_detected) ? 1U : 0U;
    uint8_t kompor_normal = (!mq6_aktif &&  flame_detected) ? 1U : 0U;

    uint8_t tekanan_leak  = (press_rate_state == 2) ? 1U : 0U;
    uint8_t tekanan_warn  = (press_rate_state == 1) ? 1U : 0U;

    /* LANGKAH 6: Kontrol Aktuator (Aktif LOW untuk LED & Buzzer) */
    HAL_GPIO_WritePin(GPIOB, PIN_LED_MERAH, (kebakaran || gas_tanpa_api) ? GPIO_PIN_RESET : GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, PIN_LED_KUNING, (kebakaran || kompor_normal) ? GPIO_PIN_RESET : GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, PIN_LED_BIRU, (tekanan_warn || tekanan_leak) ? GPIO_PIN_RESET : GPIO_PIN_SET);

    if (mq6_aktif || fan_manual_status) {
        HAL_GPIO_WritePin(GPIOB, PIN_KIPAS, GPIO_PIN_SET); /* Aktif HIGH */
    } else {
        HAL_GPIO_WritePin(GPIOB, PIN_KIPAS, GPIO_PIN_RESET);
    }

    HAL_GPIO_WritePin(GPIOB, PIN_BUZZER,
        (kebakaran || gas_tanpa_api || kompor_normal || tekanan_leak) ? GPIO_PIN_RESET : GPIO_PIN_SET);

    /* LANGKAH 7: Update Tampilan LCD I2C Sesuai Hierarki Prioritas Bahaya */
    char    lcd_msg[17];
    uint16_t kpa_int = press_kpa_x10 / 10;
    uint16_t kpa_dec = press_kpa_x10 % 10;

    /* Menggunakan overwrite spasi ("                ") alih-alih Lcd_I2c_Cmd(0x01)
       untuk mencegah layar berkedip/flicker parah setiap 250ms */

    if (kebakaran) {
    Lcd_I2c_SetCursor(0, 0); Lcd_I2c_SendString("NORMAL ");

    }
    else if (gas_tanpa_api) {
        Lcd_I2c_SetCursor(0, 0); Lcd_I2c_SendString("KOMPOR MENYALAKAN");
        Lcd_I2c_SetCursor(1, 0); Lcd_I2c_SendString("API       ");
    }
    else if (kompor_normal) {
        Lcd_I2c_SetCursor(0, 0); Lcd_I2c_SendString("GAS BOCOR");

    }
    else if (tekanan_leak) {
        Lcd_I2c_SetCursor(0, 0);
        sprintf(lcd_msg, "PRESS:%2u.%ukPa DRP", kpa_int, kpa_dec);
        Lcd_I2c_SendString(lcd_msg);
        Lcd_I2c_SetCursor(1, 0);
        sprintf(lcd_msg, "RATE:-%3u%%/s ALM", press_rate_pct);
        Lcd_I2c_SendString(lcd_msg);
    }
    else if (tekanan_warn) {
        Lcd_I2c_SetCursor(0, 0);
        sprintf(lcd_msg, "PRESS:%2u.%ukPa WRN", kpa_int, kpa_dec);
        Lcd_I2c_SendString(lcd_msg);
        Lcd_I2c_SetCursor(1, 0);
        sprintf(lcd_msg, "RATE:-%3u%%/s    ", press_rate_pct);
        Lcd_I2c_SendString(lcd_msg);
    }
    else {
        /* Kondisi Normal / Standby Aman */
        const char *label_isi;
        if      (filtered_Press >= PRESS_ADC_FULL) label_isi = "PNH";
        else if (filtered_Press >= PRESS_ADC_HALF) label_isi = "STG";
        else if (filtered_Press >= PRESS_ADC_LOW)  label_isi = "RND";
        else                                       label_isi = "KSG";

        Lcd_I2c_SetCursor(0, 0);
        sprintf(lcd_msg, "PRES:%2u.%u kPa %s ", kpa_int, kpa_dec, label_isi);
        Lcd_I2c_SendString(lcd_msg);

        Lcd_I2c_SetCursor(1, 0);
        if(fan_manual_status) {
            sprintf(lcd_msg, "SYSTEM OK [FAN] ");
        } else {
            sprintf(lcd_msg, "Kebakaran");
        }
        Lcd_I2c_SendString(lcd_msg);
    }

    HAL_Delay(250);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/* Peripheral Init Code tetap sama ke bawah... */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) Error_Handler();

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) Error_Handler();

  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) Error_Handler();
}

static void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  if (HAL_ADC_Init(&hadc1) != HAL_OK) Error_Handler();

  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
}

static void MX_I2C1_Init(void)
{
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK) Error_Handler();
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  HAL_GPIO_WritePin(GPIOB, PIN_LED_KUNING|PIN_LED_MERAH|PIN_LED_BIRU|PIN_BUZZER, GPIO_PIN_SET);
  HAL_GPIO_WritePin(GPIOB, PIN_KIPAS, GPIO_PIN_RESET);

  GPIO_InitStruct.Pin = PIN_LED_KUNING|PIN_LED_MERAH|PIN_LED_BIRU|PIN_KIPAS|PIN_BUZZER;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = PIN_FLAME|PIN_BUTTON;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void Error_Handler(void)
{
  __disable_irq();
  while (1) {}
}

c. Video Demo [kembali]



Vidio 1. Vidio Demo 

d. Kesimpulan dan Saran [kembali]
Kesimpulan
    Berdasarkan perancangan dan simulasi sistem keamanan gas LPG hunian sementara (huntara) berbasis STM32, dapat disimpulkan bahwa:
  1. Sistem keamanan gas LPG huntara berhasil dirancang menggunakan mikrokontroler STM32 sebagai pusat kendali yang membaca data sensor, mengolah informasi secara real-time, dan mengaktifkan aktuator secara otomatis berdasarkan kondisi yang terdeteksi.
  2. Sensor MQ-6 digunakan untuk mendeteksi keberadaan gas LPG di udara. Sensor ini membaca konsentrasi gas secara kontinu dan hasilnya diproses menggunakan filter moving average untuk menghasilkan pembacaan yang stabil sebelum dibandingkan dengan ambang batas yang telah ditentukan.
  3. Flame sensor digunakan untuk mendeteksi keberadaan api di sekitar area kompor atau tabung gas. Kombinasi pembacaan MQ-6 dan flame sensor memungkinkan sistem membedakan empat kondisi, yaitu kebakaran, gas bocor tanpa api, kompor menyala normal, dan kondisi aman, sehingga respons aktuator dapat disesuaikan dengan tingkat bahaya yang sesungguhnya.
  4. Sensor tekanan MPX4250 digunakan untuk memantau laju penurunan tekanan gas pada tabung LPG. Sistem menghitung perubahan tekanan setiap satu detik dan mengklasifikasikannya ke dalam tiga kategori, yaitu aman apabila penurunan kurang dari 2%, peringatan dini apabila penurunan berkisar 2–6%, dan kebocoran apabila penurunan mencapai 6% atau lebih per detik.
  5. Output sistem berupa LED merah, kuning, dan biru, buzzer, kipas angin, serta tampilan LCD 16x2 bekerja secara terkoordinasi. LED merah mengindikasikan kebocoran gas, LED kuning mengindikasikan keberadaan api, LED biru mengindikasikan anomali tekanan, buzzer memberikan peringatan suara pada kondisi berbahaya, dan kipas angin aktif secara otomatis untuk membuang akumulasi gas di ruangan.
  6. Sistem mampu membedakan kondisi peringatan dan kondisi bahaya pada sensor tekanan secara terpisah, sehingga LED biru menyala lebih awal sebagai tanda peringatan dini tanpa membunyikan buzzer, sedangkan buzzer baru aktif ketika laju penurunan tekanan sudah mencapai level berbahaya.
  7. Prototipe ini dapat digunakan sebagai simulasi awal sistem pemantauan keamanan gas LPG yang bekerja secara otomatis dengan tiga lapis deteksi, yaitu konsentrasi gas di udara, keberadaan api, dan kondisi tekanan tabung, sehingga mampu memberikan peringatan sejak dini sebelum kondisi berkembang menjadi kebakaran.
Saran
Adapun saran untuk pengembangan sistem ini adalah sebagai berikut:
  1. Pada pengembangan selanjutnya, sistem dapat ditambahkan modul komunikasi seperti Wi-Fi (ESP8266/ESP32) atau GSM agar notifikasi kondisi berbahaya dapat dikirimkan secara langsung kepada penghuni atau petugas keamanan huntara melalui pesan singkat maupun aplikasi.
  2. Sensor MQ-6 perlu melalui proses pemanasan awal yang cukup sebelum digunakan agar pembacaan konsentrasi gas lebih stabil dan tidak menghasilkan nilai yang tidak akurat pada saat pertama kali sistem dinyalakan, terutama di lingkungan dengan suhu dan kelembapan yang berubah-ubah.
  3. Sensor MPX4250 sebaiknya dikalibrasi sesuai karakteristik tabung LPG yang digunakan agar nilai ambang batas tekanan dan persentase penurunan yang ditetapkan mencerminkan kondisi nyata tabung, sehingga sistem tidak terlalu sering memberikan peringatan palsu maupun terlambat mendeteksi kebocoran.
  4. Flame sensor sebaiknya ditempatkan pada posisi yang memiliki sudut pandang langsung ke area kompor dan tidak terhalang benda lain, agar deteksi api lebih andal dan tidak terpengaruh oleh pantulan cahaya atau sumber panas lain di sekitar dapur huntara.
  5. Seluruh rangkaian elektronik dan sensor sebaiknya ditempatkan di dalam kotak pelindung yang sesuai untuk mengurangi risiko kerusakan akibat uap minyak, kelembapan tinggi, dan kondisi lingkungan dapur yang rentan terhadap kotoran dan panas.
  6. Kipas angin yang digunakan sebaiknya dipilih berdasarkan kapasitas ruang dapur huntara agar dapat membuang akumulasi gas secara efektif. Pemasangan kipas juga perlu memperhatikan arah aliran udara agar gas yang terdeteksi dapat dikeluarkan menuju ventilasi, bukan justru menyebar ke dalam ruangan.
  7. Untuk penerapan nyata, sistem perlu dilengkapi sumber daya cadangan seperti baterai agar tetap dapat beroperasi ketika pasokan listrik di huntara terganggu, mengingat kondisi darurat seperti kebocoran gas justru berpotensi terjadi bersamaan dengan gangguan kelistrikan.
  8. Sistem dapat dikembangkan dengan menambahkan penyimpanan data untuk mencatat riwayat kejadian, seperti waktu terdeteksinya gas bocor, nilai tekanan saat alarm berbunyi, dan frekuensi kondisi peringatan, agar data tersebut dapat dianalisis untuk evaluasi keamanan huntara secara berkala.
e. Download file [kembali]
    Download Rangkaian Proteus klik disini
    Download Video Simulasi klik disini
    Download Datasheet Buzzer klik disini
    Download Datasheet LED Red klik disini
    Download Datasheet LED Blue klik disini
    Download Datasheet LED Yellow klik disini
    Download Datasheet Fan klik disini
    Download Datasheet Sensor MQ-6 klik disini
    Download Datasheet Sensor Vibrator klik disini
    Download Datasheet Sensor Flame klik disini
    Download Datasheet Display I2C klik disini
    Download Datasheet resistor klik disini
    Download Datasheet Potensiometer klik disini
    Download Library Sensor Flame klik disini
    Download Library Sensor Gas klik disini
    Download Library STM32 Proteus klik disini

<

Komentar

Postingan populer dari blog ini

Modul 2 Transistor

Modul 4 Filter