Modul 4




MODUL 4

Rancang Bangun Prototype Sistem Peringan Dini Longsor Berbasi STM32 Sebagai Media Edukasi Kebencanaan di Kawasan Batu Busuk, Padang


1. Pendahuluan [kembali]

Tanah longsor merupakan salah satu bencana alam yang sering terjadi di Indonesia dan berpotensi menimbulkan kerugian baik dari segi material maupun keselamatan jiwa. Tingginya intensitas curah hujan, kondisi geologi yang beragam, serta banyaknya wilayah dengan topografi berbukit menyebabkan tingkat kerawanan longsor di Indonesia relatif tinggi. Salah satu wilayah yang memiliki potensi terjadinya longsor adalah kawasan Batu Busuk, Kota Padang, yang ditandai dengan kemiringan lereng yang cukup curam serta curah hujan yang tinggi sepanjang tahun. Kondisi tersebut dapat memicu ketidakstabilan tanah sehingga meningkatkan risiko terjadinya longsor, terutama pada musim penghujan.

Bencana longsor umumnya terjadi secara tiba-tiba dan sulit diprediksi tanpa adanya pemantauan kondisi lingkungan secara berkelanjutan. Keterbatasan sistem pemantauan dan peringatan dini di kawasan rawan longsor menyebabkan masyarakat sekitar memiliki waktu yang sangat terbatas untuk melakukan tindakan mitigasi ketika terjadi indikasi bahaya. Oleh karena itu, diperlukan suatu sistem yang mampu mendeteksi gejala awal longsor sehingga dapat memberikan informasi dan peringatan kepada masyarakat sebelum bencana terjadi.

Perkembangan teknologi mikrokontroler dan sensor saat ini membuka peluang untuk mengembangkan sistem peringatan dini yang lebih sederhana, ekonomis, dan mudah diterapkan. Mikrokontroler STM32 merupakan salah satu platform yang memiliki kemampuan pemrosesan data yang baik serta mendukung integrasi dengan berbagai sensor. Dengan memanfaatkan sensor getaran, sensor hujan, dan sensor kemiringan (tilt), kondisi lingkungan yang berpotensi memicu longsor dapat dipantau secara real-time. Informasi yang diperoleh kemudian dapat ditampilkan melalui LCD serta disertai peringatan visual dan audio menggunakan LED dan buzzer.

Berdasarkan permasalahan tersebut, pada proyek ini dirancang sebuah prototype sistem peringatan dini longsor berbasis STM32 yang mampu memantau parameter getaran, curah hujan, dan kemiringan lereng sebagai indikator potensi longsor. Selain berfungsi sebagai purwarupa sistem monitoring, perangkat yang dikembangkan juga diharapkan dapat menjadi media edukasi kebencanaan yang membantu meningkatkan pemahaman masyarakat dan pelajar mengenai pentingnya mitigasi bencana serta pemanfaatan teknologi dalam upaya pengurangan risiko longsor, khususnya di kawasan Batu Busuk, Kota Padang.

2. Tujuan [kembali]

    Tujuan dari pembuatan prototipe ini adalah:

1.    Merancang dan membangun prototype sistem peringatan dini longsor berbasis mikrokontroler STM32.

2.    Mengimplementasikan pembacaan tiga parameter utama penyebab longsor, yaitu getaran tanah, curah hujan, dan kemiringan lereng (tilt, pitch, roll), menggunakan sensor getaran, sensor hujan, dan accelerometer ADXL345.

3.    Merancang sistem peringatan bertingkat (normal, waspada, dan bahaya) berupa indikator LED, pola bunyi buzzer yang berbeda untuk tiap tingkat bahaya, serta tampilan informasi pada LCD 16x2.

4.    Menjadikan prototype ini sebagai media edukasi kebencanaan yang interaktif dan mudah dipahami, khususnya bagi masyarakat di kawasan Batu Busuk, Padang.


3. Alat dan Komponen [kembali]

  • Hardware 

        a) Mikrokontroler STM32F103C8T6




         b) Vibration Sensor



         c) Rain Sensor


         d) Sensor Accelerometer ADXL345

        e) LCD 16 X 2

       f) Buzzer

        g) LED Merah, Kuning dan Hijau


        h) Breadboard 

         i) Jamper
      j) Resistor


    k) ST Link

      

4. Landasan Teori [kembali]

    4.1 Tanah Longsor dan Sistem Peringatan Dini

Tanah longsor (landslide) adalah gerakan massa tanah, batuan, atau kombinasi keduanya yang bergerak turun secara gravitasional. Faktor pemicu utama tanah longsor meliputi: curah hujan intensitas tinggi, getaran akibat gempa, kemiringan lereng yang kritis, dan saturasi air tanah yang berlebihan. Di Indonesia, tanah longsor termasuk dalam bencana hidrometeorologi yang paling sering terjadi, terutama di wilayah dengan topografi berbukit seperti Sumatera Barat.

Sistem Peringatan Dini (Early Warning System / EWS) adalah serangkaian prosedur dan mekanisme teknis yang dirancang untuk mendeteksi tanda-tanda awal bencana dan menyebarkan peringatan kepada masyarakat sebelum bencana terjadi. Komponen utama EWS meliputi: (1) sensor/instrumen monitoring, (2) unit pemrosesan data, (3) sistem komunikasi, dan (4) protokol respons masyarakat. 

4.2 Mikrokontroler STM32F103C8T6

STM32F103C8T6 (Blue Pill) adalah mikrokontroler berbasis arsitektur ARM Cortex-M3 32-bit yang diproduksi oleh STMicroelectronics. Spesifikasi utamanya adalah:

          Kecepatan clock maksimum: 72 MHz

          Memori Flash: 64 KB untuk penyimpanan program

          Memori SRAM: 20 KB untuk data runtime

          Jumlah pin GPIO: 37 pin yang dapat dikonfigurasi

          ADC 12-bit: 10 channel untuk pembacaan sensor analog

          Komunikasi: UART, SPI, I2C, USB, CAN

          Tegangan operasi: 2.0V - 3.6V

          Package: LQFP48

STM32CubeIDE merupakan Integrated Development Environment (IDE) resmi dari STMicroelectronics yang mengintegrasikan STM32CubeMX untuk konfigurasi hardware dan Eclipse IDE untuk pengembangan kode. Pemrograman dilakukan menggunakan bahasa C dengan dukungan HAL (Hardware Abstraction Layer) library. 

4.3 Sensor Vibration (SW-420)

Sensor SW-420 merupakan modul sensor getaran yang memanfaatkan komponen pegas logam (spring) yang terhubung ke elektroda. Ketika terjadi getaran atau guncangan, pegas bergerak dan menyentuh elektroda kontak, menghasilkan sinyal listrik. Modul ini dilengkapi komparator LM393 dan potensiometer untuk mengatur threshold sensitivitas. Output yang dihasilkan adalah sinyal digital: HIGH (logika 1) saat getaran terdeteksi di atas threshold, dan LOW (logika 0) saat tidak ada getaran. 

Pada sistem ini, sensor getaran dihubungkan ke pin PA0 STM32 yang dikonfigurasi sebagai input digital dengan pull-down resistor internal. Kondisi HIGH pada PA0 memiliki PRIORITAS TERTINGGI (Prioritas 1) dalam logika sistem, langsung memicu status BAHAYA dengan mengaktifkan LED merah, buzzer, dan menampilkan pesan "BAHAYA! / Getaran Kuat" pada LCD. 

Tabel 3.1  Karakteristik Respon Sensor Getaran SW-420

Kondisi Tanah

Intensitas Getaran

Output Digital

Respon Sistem

Diam / Tenang

Tidak ada (0 mm/s)

LOW (0)

Normal — LED Hijau

Getaran Ringan

< Threshold SW-420

LOW (0)

Normal — tidak ada respons

Getaran Sedang

Mendekati threshold

LOW (0)

Normal — belum terpicu

Getaran Kuat

> Threshold (diset)

HIGH (1)

BAHAYA — LED Merah + Buzzer ON

Getaran Ekstrem

Jauh di atas threshold

HIGH (1)

BAHAYA — Prioritas 1 aktif

 

Grafik perbandingan frekuensi dengan sensitivitas sensor getaran :



 

4.4 Sensor Hujan (Rain Sensor FC-37)

Sensor hujan FC-37 bekerja berdasarkan prinsip perubahan konduktivitas listrik pada permukaan pelat bergalur (PCB sensing pad) saat terkena air hujan. Semakin banyak air yang menempel pada pelat, semakin rendah nilai resistansinya dan semakin tinggi arus yang mengalir. Modul ini menghasilkan dua jenis output: output analog (tegangan 0–3,3 V, berbanding terbalik dengan intensitas hujan) dan output digital (HIGH saat hujan melampaui threshold yang diset melalui potensiometer). 

Dalam sistem ini digunakan output digital yang dihubungkan ke pin PA1 STM32. Kondisi HIGH pada PA1 memiliki PRIORITAS KEDUA dalam logika sistem, memicu status BAHAYA dengan pesan "BAHAYA! / Hujan Deras" pada LCD beserta aktivasi LED merah dan buzzer. 

Tabel 3.2  Karakteristik Respon Sensor Hujan FC-37

Kondisi Hujan

Tegangan Analog

Output Digital

Respon Sistem

Tidak hujan (kering)

~3,3 V

LOW (0)

Normal — sistem lanjut ke sensor kemiringan

Gerimis ringan

~2,5–3,0 V

LOW (0)

Normal — di bawah threshold

Hujan sedang

~1,2–2,5 V

LOW/HIGH

Tergantung setting threshold

Hujan deras

< 0,8 V (threshold)

HIGH (1)

BAHAYA — LED Merah + Buzzer ON + LCD


Grafik respon rain sensor:

4.5 SensorSensor Accelerometer ADXL345

ADXL345 merupakan sensor accelerometer MEMS 3 sumbu yang berkomunikasi melalui protokol I2C. Sensor ini menghasilkan data percepatan pada sumbu X, Y, dan Z yang kemudian diolah menggunakan fungsi trigonometri (atan2 dan sqrt) untuk menghitung sudut Pitch (kemiringan depan-belakang), Roll (kemiringan kiri-kanan), dan Tilt (kemiringan total) suatu permukaan, sehingga dapat digunakan untuk memantau pergerakan atau kemiringan lereng.

           Grafik respon sensor: 



4.6 LCD 16x2 dengan Interface I2C (PCF8574)

LCD (Liquid Crystal Display) 16x2 mampu menampilkan 16 karakter dalam 2 baris teks. Module I2C PCF8574 yang terintegrasi memungkinkan komunikasi hanya menggunakan 2 pin (SDA dan SCL), menghemat penggunaan GPIO STM32. Alamat I2C default adalah 0x27 atau 0x3F tergantung posisi jumper. Pada sistem ini, LCD digunakan untuk menampilkan nilai sensor secara real-time dan status level peringatan.

4.7  Sistem Peringatan Dini Bencana (Early Warning System)

Sistem peringatan dini bencana adalah rangkaian kegiatan pemberian peringatan sesegera mungkin kepada masyarakat tentang kemungkinan terjadinya bencana. Pada prototype ini, konsep tersebut diwujudkan dalam bentuk peringatan bertingkat (normal, waspada, bahaya) yang ditampilkan melalui kombinasi warna LED, pola bunyi buzzer, dan informasi tekstual pada LCD.


5. Flowchart dan Listing Program [kembali]

  • Flowchart






  • Listing Program

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Deteksi Longsor dengan ADXL345, Sensor Getaran, Hujan (digital)
  *                   LCD I2C 16x2 dengan perbedaan bunyi buzzer
  *                   Menampilkan Tilt, Pitch, Roll di LCD
  *                   RAIN SENSOR: ACTIVE LOW (0 = Hujan, 1 = Tidak Hujan)
  *                   Pin sesuai CubeMX (label yang sudah ditentukan)
  ******************************************************************************
  */
/* USER CODE END Header */

#include "main.h"
#include "stm32f1xx_hal.h"
#include <stdio.h>
#include <string.h>
#include <math.h>

/* Private define ------------------------------------------------------------*/
#define LCD_ADDR_A              0x27
#define LCD_ADDR_B              0x3F
#define LCD_ADDR_C              0x26
#define LCD_ADDR_D              0x20
#define LCD_BL                  0x08
#define LCD_EN                  0x04
#define LCD_RS                  0x01

#define ADXL345_ADDR            0x53
#define ADXL345_REG_DEVID       0x00
#define ADXL345_REG_POWER_CTL   0x2D
#define ADXL345_REG_DATA_FORMAT 0x31
#define ADXL345_REG_DATAX0      0x32
#define ADXL345_DEVID_VALUE     0xE5

#define ACCEL_SENSITIVITY       256.0f
#define RAD_TO_DEG              57.29578f
#define TILT_THRESHOLD_WARNING  10.0f
#define TILT_THRESHOLD_DANGER   30.0f

/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim2;
static uint8_t lcd_addr = 0;

volatile uint8_t emergencyMode = 0;
uint32_t lastButtonTime = 0;
float tilt_angle, pitch_angle, roll_angle;
int16_t accel_x, accel_y, accel_z;
uint8_t displayPage = 0;
uint32_t lastPageSwitch = 0;

/* Variabel untuk Buzzer dengan pola berbeda */
uint32_t buzzerOnTime = 0;
uint32_t buzzerOffTime = 0;
uint32_t lastBuzzerToggle = 0;
uint8_t buzzerActive = 0;

/* Function prototypes -------------------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_TIM2_Init(void);

/* ==================== Microsecond delay ==================== */
void delay_us(uint16_t us) {
    __HAL_TIM_SET_COUNTER(&htim2, 0);
    while (__HAL_TIM_GET_COUNTER(&htim2) < us);
}

/* ==================== LCD I2C Driver untuk 16x2 ==================== */
void LCD_SendI2C(uint8_t data) {
    uint8_t buffer[1];
    buffer[0] = data;
    
    // Tambah retry mechanism
    for(int retry = 0; retry < 3; retry++) {
        if(HAL_I2C_Master_Transmit(&hi2c1, lcd_addr << 1, buffer, 1, 100) == HAL_OK) {
            break;
        }
        HAL_Delay(1);
    }
}

void LCD_SendNibble(uint8_t nibble, uint8_t rs) {
    uint8_t d = (nibble & 0xF0) | LCD_BL | (rs ? LCD_RS : 0);
    
    // Pulse EN yang benar
    LCD_SendI2C(d | LCD_EN);
    delay_us(10);
    LCD_SendI2C(d & ~LCD_EN);
    delay_us(100);
}

void LCD_SendCmd(uint8_t cmd) {
    LCD_SendNibble(cmd & 0xF0, 0);
    LCD_SendNibble((cmd << 4) & 0xF0, 0);
    HAL_Delay(2);
}

void LCD_SendData(uint8_t data) {
    LCD_SendNibble(data & 0xF0, 1);
    LCD_SendNibble((data << 4) & 0xF0, 1);
    HAL_Delay(1);
}

void LCD_Init(void) {
    // Cek alamat I2C yang umum digunakan
    uint8_t addresses[] = {0x27, 0x3F, 0x26, 0x20};
    uint8_t found = 0;
    
    for(int i = 0; i < 4; i++) {
        if(HAL_I2C_IsDeviceReady(&hi2c1, addresses[i] << 1, 1, 100) == HAL_OK) {
            lcd_addr = addresses[i];
            found = 1;
            break;
        }
    }
    
    if(!found) {
        // LCD tidak terdeteksi, LED merah berkedip cepat
        while(1) {
            HAL_GPIO_TogglePin(LED_RED_Pin_GPIO_Port, LED_RED_Pin_Pin);
            HAL_Delay(100);
        }
    }
    
    // Delay untuk power up LCD
    HAL_Delay(50);
    
    // Sequence initialization yang benar untuk LCD 16x2
    LCD_SendNibble(0x30, 0); HAL_Delay(5);
    LCD_SendNibble(0x30, 0); HAL_Delay(1);
    LCD_SendNibble(0x30, 0); HAL_Delay(1);
    LCD_SendNibble(0x20, 0); HAL_Delay(1);
    
    // Function set: 4-bit, 2 lines, 5x8 dots
    LCD_SendCmd(0x28);
    HAL_Delay(1);
    
    // Display on/off control: display on, cursor off, blink off
    LCD_SendCmd(0x0C);
    HAL_Delay(1);
    
    // Entry mode set: increment, no shift
    LCD_SendCmd(0x06);
    HAL_Delay(1);
    
    // Clear display
    LCD_SendCmd(0x01);
    HAL_Delay(5);
    
    // Test pattern untuk verifikasi
    LCD_SetCursor(0, 0);
    LCD_Print("LCD Ready");
    HAL_Delay(1000);
    LCD_Clear();
}

void LCD_Clear(void) {
    LCD_SendCmd(0x01);
    HAL_Delay(2);
}

void LCD_SetCursor(uint8_t col, uint8_t row) {
    uint8_t addr;
    if(row == 0) {
        addr = 0x80 + col;
    } else {
        addr = 0xC0 + col;
    }
    LCD_SendCmd(addr);
}

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

/* ==================== ADXL345 ==================== */
uint8_t ADXL345_Init(void) {
    uint8_t dev_id;
    if (HAL_I2C_Mem_Read(&hi2c1, ADXL345_ADDR << 1, ADXL345_REG_DEVID,
                         I2C_MEMADD_SIZE_8BIT, &dev_id, 1, 100) != HAL_OK)
        return 0;
    if (dev_id != ADXL345_DEVID_VALUE) return 0;

    uint8_t data = 0x08;
    HAL_I2C_Mem_Write(&hi2c1, ADXL345_ADDR << 1, ADXL345_REG_POWER_CTL,
                      I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
    HAL_Delay(10);
    data = 0x08;
    HAL_I2C_Mem_Write(&hi2c1, ADXL345_ADDR << 1, ADXL345_REG_DATA_FORMAT,
                      I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
    HAL_Delay(10);
    return 1;
}

void ADXL345_Read_Accel(int16_t *x, int16_t *y, int16_t *z) {
    uint8_t buffer[6];
    if (HAL_I2C_Mem_Read(&hi2c1, ADXL345_ADDR << 1, ADXL345_REG_DATAX0,
                         I2C_MEMADD_SIZE_8BIT, buffer, 6, 100) == HAL_OK) {
        *x = (int16_t)((buffer[1] << 8) | buffer[0]);
        *y = (int16_t)((buffer[3] << 8) | buffer[2]);
        *z = (int16_t)((buffer[5] << 8) | buffer[4]);
    } else {
        *x = *y = *z = 0;
    }
}

void ADXL345_Calculate_Angles(int16_t x, int16_t y, int16_t z,
                              float *pitch, float *roll, float *tilt) {
    float ax = x / ACCEL_SENSITIVITY;
    float ay = y / ACCEL_SENSITIVITY;
    float az = z / ACCEL_SENSITIVITY;
    *pitch = atan2f(ay, sqrtf(ax*ax + az*az)) * RAD_TO_DEG;
    *roll  = atan2f(ax, sqrtf(ay*ay + az*az)) * RAD_TO_DEG;
    *tilt  = sqrtf((*pitch)*(*pitch) + (*roll)*(*roll));
    if (*tilt < 0) *tilt = -*tilt;
}

/* ==================== LED control ==================== */
void LED_OFF_ALL(void) {
    HAL_GPIO_WritePin(LED_RED_Pin_GPIO_Port, LED_RED_Pin_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LED_YELLOW_Pin_GPIO_Port, LED_YELLOW_Pin_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LED_GREEN_Pin_GPIO_Port, LED_GREEN_Pin_Pin, GPIO_PIN_RESET);
}

/* ==================== Buzzer dengan Pola Berbeda ==================== */
void SetBuzzerPattern(uint8_t priority, float tilt_angle) {
    switch(priority) {
        case 1:  // Bahaya ekstrim (getaran/emergency)
            buzzerOnTime = 1000;   // 1 detik ON
            buzzerOffTime = 500;   // 0.5 detik OFF
            break;
            
        case 2:  // Waspada (hujan deras)
            buzzerOnTime = 250;    // 0.25 detik ON
            buzzerOffTime = 250;   // 0.25 detik OFF
            break;
            
        case 3:  // Kemiringan tanah
            if (tilt_angle > TILT_THRESHOLD_DANGER) {
                // Danger > 30°
                buzzerOnTime = 1000;   // 1 detik ON
                buzzerOffTime = 500;   // 0.5 detik OFF
            } 
            else if (tilt_angle > TILT_THRESHOLD_WARNING) {
                // Warning 10-30°
                buzzerOnTime = 500;    // 0.5 detik ON
                buzzerOffTime = 500;   // 0.5 detik OFF
            }
            else {
                buzzerOnTime = 0;
                buzzerOffTime = 0;
            }
            break;
            
        default:  // Priority 0 (Normal)
            buzzerOnTime = 0;
            buzzerOffTime = 0;
            break;
    }
}

void UpdateBuzzer(uint8_t priority, float tilt_angle, uint32_t currentTime) {
    SetBuzzerPattern(priority, tilt_angle);
    
    // Jika tidak ada alarm (OnTime = 0)
    if (buzzerOnTime == 0) {
        HAL_GPIO_WritePin(BUZZER_Pin_GPIO_Port, BUZZER_Pin_Pin, GPIO_PIN_RESET);
        buzzerActive = 0;
        lastBuzzerToggle = currentTime;
        return;
    }
    
    // Logika toggle buzzer
    if (buzzerActive == 0) {
        // Buzzer OFF, cek apakah sudah waktunya ON
        if ((currentTime - lastBuzzerToggle) >= buzzerOffTime) {
            buzzerActive = 1;
            lastBuzzerToggle = currentTime;
            HAL_GPIO_WritePin(BUZZER_Pin_GPIO_Port, BUZZER_Pin_Pin, GPIO_PIN_SET);
        }
    } else {
        // Buzzer ON, cek apakah sudah waktunya OFF
        if ((currentTime - lastBuzzerToggle) >= buzzerOnTime) {
            buzzerActive = 0;
            lastBuzzerToggle = currentTime;
            HAL_GPIO_WritePin(BUZZER_Pin_GPIO_Port, BUZZER_Pin_Pin, GPIO_PIN_RESET);
        }
    }
}

/* ==================== LCD Display Update untuk 16x2 ==================== */
void Update_LCD_Display(uint8_t priority, uint8_t isVibration, uint8_t isRain) {
    char line1[17], line2[17];
    char buffer[17];
    
    // ==================== BARIS 1 ====================
    if (emergencyMode) {
        sprintf(line1, "DARURAT MANUAL!");
    }
    else if (priority == 1) {
        sprintf(line1, "GETARAN KUAT!  ");
    }
    else if (priority == 2) {
        sprintf(line1, "HUJAN DERAS!   ");
    }
    else if (priority == 3) {
        if (tilt_angle <= TILT_THRESHOLD_WARNING) {
            sprintf(line1, "NORMAL         ");
        }
        else if (tilt_angle <= TILT_THRESHOLD_DANGER) {
            sprintf(line1, "WASPADA!       ");
        }
        else {
            sprintf(line1, "!!! LONGSOR !!!");
        }
    }
    else {
        sprintf(line1, "SISTEM OK      ");
    }
    
    // ==================== BARIS 2 ====================
    if (emergencyMode) {
        sprintf(line2, "TEKAN TOMBOL   ");
    }
    else if (priority == 1) {
        sprintf(line2, "BAHAYA LANGSUNG!");
    }
    else if (priority == 2) {
        sprintf(line2, "POTENSI LONGSOR");
    }
    else {
        // Tampilkan nilai kemiringan (Tilt, Pitch, Roll) dengan 3 halaman
        if (displayPage == 0) {
            // Halaman 1: Tilt (kemiringan total)
            sprintf(buffer, "Tilt:%.1f%c     ", tilt_angle, 0xDF);  // 0xDF = derajat
            strncpy(line2, buffer, 16);
            line2[16] = '\0';
        }
        else if (displayPage == 1) {
            // Halaman 2: Pitch (kemiringan depan-belakang)
            sprintf(buffer, "Pitch:%.1f%c   ", pitch_angle, 0xDF);
            strncpy(line2, buffer, 16);
            line2[16] = '\0';
        }
        else {
            // Halaman 3: Roll (kemiringan kiri-kanan)
            sprintf(buffer, "Roll:%.1f%c    ", roll_angle, 0xDF);
            strncpy(line2, buffer, 16);
            line2[16] = '\0';
        }
    }
    
    // Tampilkan ke LCD
    LCD_SetCursor(0, 0);
    LCD_Print(line1);
    LCD_SetCursor(0, 1);
    LCD_Print(line2);
    
    // Tampilkan indikator halaman di baris 1 kolom 15 (paling kanan)
    if (priority == 0 || priority == 3) {
        LCD_SetCursor(15, 0);
        if (displayPage == 0) {
            LCD_Print("1");
        } else if (displayPage == 1) {
            LCD_Print("2");
        } else {
            LCD_Print("3");
        }
    } else {
        LCD_SetCursor(15, 0);
        LCD_Print(" ");
    }
    
    // Tampilkan indikator emergency di baris 2 kolom 15
    if (emergencyMode) {
        LCD_SetCursor(15, 1);
        LCD_Print("E");
    } else {
        LCD_SetCursor(15, 1);
        LCD_Print(" ");
    }
    
    // Tampilkan status sensor di pojok kiri bawah (kolom 0-1)
    LCD_SetCursor(0, 1);
    if (isVibration) {
        LCD_Print("V");
    } else {
        LCD_Print(" ");
    }
    LCD_SetCursor(1, 1);
    if (isRain) {
        LCD_Print("R");
    } else {
        LCD_Print(" ");
    }
}

/* ==================== Tampilan Inisialisasi ==================== */
void Show_Init_Screens(void) {
    LCD_Clear();
    LCD_Print("Sistem Deteksi");
    LCD_SetCursor(0, 1);
    LCD_Print("Longsor Ready  ");
    HAL_Delay(2000);
    
    LCD_Clear();
    LCD_Print("Init ADXL345...");
    HAL_Delay(1000);
    
    if (!ADXL345_Init()) {
        LCD_Clear();
        LCD_Print("ADXL345 ERROR! ");
        LCD_SetCursor(0, 1);
        LCD_Print("Cek Koneksi!   ");
        while (1) {
            HAL_GPIO_TogglePin(LED_RED_Pin_GPIO_Port, LED_RED_Pin_Pin);
            HAL_Delay(500);
        }
    }
    
    LCD_Clear();
    LCD_Print("ADXL345 OK!    ");
    HAL_Delay(2000);
    
    LCD_Clear();
    LCD_Print("PRIORITAS:     ");
    LCD_SetCursor(0, 1);
    LCD_Print("1.VIB 2.RAIN 3.");
    HAL_Delay(3000);
    
    LCD_Clear();
    LCD_Print("Mode: NORMAL   ");
    LCD_SetCursor(0, 1);
    LCD_Print("Tekan Tombol   ");
    HAL_Delay(2000);
    
    LCD_Clear();
    LCD_Print("KEMIRINGAN:    ");
    LCD_SetCursor(0, 1);
    LCD_Print("Pg1:Tilt Pg2:Pt");
    HAL_Delay(2000);
    LCD_Clear();
    LCD_Print("Pg3:Roll      ");
    LCD_SetCursor(0, 1);
    LCD_Print("Auto ganti 3dtk");
    HAL_Delay(2000);
    LCD_Clear();
}

/* ==================== I2C Scanner untuk Diagnostik ==================== */
void I2C_Scanner(void) {
    char buffer[17];
    uint8_t found = 0;
    
    LCD_Clear();
    LCD_Print("Scan I2C...    ");
    HAL_Delay(1000);
    
    for(uint8_t addr = 1; addr < 127; addr++) {
        if(HAL_I2C_IsDeviceReady(&hi2c1, addr << 1, 1, 100) == HAL_OK) {
            sprintf(buffer, "0x%02X Found   ", addr);
            LCD_Clear();
            LCD_Print(buffer);
            HAL_Delay(1000);
            found = 1;
        }
    }
    
    if(!found) {
        LCD_Clear();
        LCD_Print("No I2C Device!");
        HAL_Delay(2000);
    }
}

/* ==================== Interrupt callback ==================== */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == PUSH_BUTTON_Pin_Pin) {
        uint32_t now = HAL_GetTick();
        if ((now - lastButtonTime) > 300) {
            lastButtonTime = now;
            emergencyMode = !emergencyMode;
            
            // Reset buzzer saat mode berubah
            buzzerActive = 0;
            lastBuzzerToggle = HAL_GetTick();
            HAL_GPIO_WritePin(BUZZER_Pin_GPIO_Port, BUZZER_Pin_Pin, GPIO_PIN_RESET);
        }
    }
}

/* ==================== Main Program ==================== */
int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_TIM2_Init();

    HAL_TIM_Base_Start(&htim2);
    LCD_Init();
    
    /* Opsional: Jalankan I2C scanner untuk diagnostik */
    // I2C_Scanner();
    
    Show_Init_Screens();

    uint32_t lastDisplay = 0, lastADXL = 0, lastBuzzerUpdate = 0;
    uint8_t isVib = 0, isRain = 0, priority = 0;

    while (1) {
        uint32_t now = HAL_GetTick();
        
        // Baca ADXL345 setiap 100ms
        if ((now - lastADXL) >= 100) {
            lastADXL = now;
            ADXL345_Read_Accel(&accel_x, &accel_y, &accel_z);
            ADXL345_Calculate_Angles(accel_x, accel_y, accel_z,
                                    &pitch_angle, &roll_angle, &tilt_angle);
        }
        
        // Update buzzer setiap 50ms (responsif untuk pola suara)
        if ((now - lastBuzzerUpdate) >= 50) {
            lastBuzzerUpdate = now;
            UpdateBuzzer(priority, tilt_angle, now);
        }
        
        // Update display setiap 500ms
        if ((now - lastDisplay) >= 500) {
            lastDisplay = now;
            
            // Baca sensor digital
            // VIBRATION_SENSOR: Active High (1 = getaran terdeteksi)
            isVib = HAL_GPIO_ReadPin(VIBRATION_SENSOR_Pin_GPIO_Port, VIBRATION_SENSOR_Pin_Pin);
            
            // RAIN_SENSOR: ACTIVE LOW (0 = hujan, 1 = tidak hujan)
            isRain = (HAL_GPIO_ReadPin(RAIN_SENSOR_Pin_GPIO_Port, RAIN_SENSOR_Pin_Pin) == GPIO_PIN_RESET) ? 1 : 0;
            
            // Ganti halaman setiap 3 detik (3 halaman untuk normal/miring)
            if ((now - lastPageSwitch) >= 3000) {
                lastPageSwitch = now;
                // Ganti halaman hanya dalam mode normal atau kemiringan
                if (priority == 0 || priority == 3) {
                    displayPage++;
                    if (displayPage > 2) {
                        displayPage = 0;
                    }
                }
            }

            // Tentukan prioritas (Hierarki: Emergency > Getaran > Hujan > Kemiringan)
            if (emergencyMode) {
                priority = 1;
            }
            else if (isVib) {
                priority = 1;
            }
            else if (isRain) {
                priority = 2;
            }
            else if (tilt_angle > TILT_THRESHOLD_DANGER) {
                priority = 3;
            }
            else if (tilt_angle > TILT_THRESHOLD_WARNING) {
                priority = 3;
            }
            else {
                priority = 0;
            }

            // Kontrol LED berdasarkan prioritas
            LED_OFF_ALL();
            if (priority == 1) {
                // Bahaya ekstrim - LED Merah
                HAL_GPIO_WritePin(LED_RED_Pin_GPIO_Port, LED_RED_Pin_Pin, GPIO_PIN_SET);
            }
            else if (priority == 2) {
                // Waspada hujan - LED Kuning
                HAL_GPIO_WritePin(LED_YELLOW_Pin_GPIO_Port, LED_YELLOW_Pin_Pin, GPIO_PIN_SET);
            }
            else if (priority == 3) {
                // Kemiringan tanah
                if (tilt_angle > TILT_THRESHOLD_DANGER) {
                    // Danger > 30° - LED Merah
                    HAL_GPIO_WritePin(LED_RED_Pin_GPIO_Port, LED_RED_Pin_Pin, GPIO_PIN_SET);
                } else {
                    // Warning 10-30° - LED Kuning
                    HAL_GPIO_WritePin(LED_YELLOW_Pin_GPIO_Port, LED_YELLOW_Pin_Pin, GPIO_PIN_SET);
                }
            }
            else {
                // Normal - LED Hijau
                HAL_GPIO_WritePin(LED_GREEN_Pin_GPIO_Port, LED_GREEN_Pin_Pin, GPIO_PIN_SET);
            }
            
            // Update LCD
            Update_LCD_Display(priority, isVib, isRain);
        }
        
        HAL_Delay(10);
    }
}

/* ==================== HAL Initialization (CubeMX generated) ==================== */
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {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();
}

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_TIM2_Init(void) {
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 7;
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 65535;
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_Base_Init(&htim2) != HAL_OK) Error_Handler();
    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) Error_Handler();
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != 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, LED_RED_Pin_Pin | LED_YELLOW_Pin_Pin | LED_GREEN_Pin_Pin | BUZZER_Pin_Pin, GPIO_PIN_RESET);
    
    /* Input pins */
    GPIO_InitStruct.Pin = VIBRATION_SENSOR_Pin_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;  // Pull-down untuk active high
    HAL_GPIO_Init(VIBRATION_SENSOR_Pin_GPIO_Port, &GPIO_InitStruct);
    
    GPIO_InitStruct.Pin = RAIN_SENSOR_Pin_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;    // Pull-up untuk active low
    HAL_GPIO_Init(RAIN_SENSOR_Pin_GPIO_Port, &GPIO_InitStruct);
    
    GPIO_InitStruct.Pin = PUSH_BUTTON_Pin_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(PUSH_BUTTON_Pin_GPIO_Port, &GPIO_InitStruct);
    
    /* Output pins */
    GPIO_InitStruct.Pin = LED_RED_Pin_Pin | LED_YELLOW_Pin_Pin | LED_GREEN_Pin_Pin | BUZZER_Pin_Pin;
    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);
    
    /* Enable interrupts */
    HAL_NVIC_SetPriority(PUSH_BUTTON_Pin_EXTI_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(PUSH_BUTTON_Pin_EXTI_IRQn);
}

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

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line) {}
#endif


6. Rangkaian Simulasi dan Prinsip Kerja [kembali]

  • Rangkaian Simulasi

  • Prinsip Kerja
Prinsip kerja rangkaian prototipe sistem peringatan dini longsor berbasis STM32 ini dimulai dengan mikrokontroler STM32 yang secara terus-menerus melakukan pembacaan data dari tiga sensor, yaitu sensor getaran (vibration sensor), sensor hujan (rain sensor), dan sensor kemiringan lereng (tilt sensor yang disimulasikan menggunakan potensiometer). Sistem menerapkan metode prioritas dalam proses deteksinya, di mana sensor getaran memiliki prioritas tertinggi, diikuti sensor hujan, dan terakhir sensor kemiringan. Ketika sensor getaran yang terhubung pada pin PA0 mendeteksi getaran kuat, STM32 langsung mengidentifikasinya sebagai kondisi bahaya, kemudian mengaktifkan LED merah pada pin PB0 dan buzzer pada pin PB11 sebagai alarm peringatan. Selain itu, LCD 16×2 akan menampilkan pesan "BAHAYA!" dan "Getaran Kuat" untuk memberikan informasi kepada pengguna mengenai kondisi yang terjadi.

Apabila sensor getaran tidak mendeteksi adanya getaran berbahaya, sistem akan melanjutkan pembacaan sensor hujan yang terhubung pada pin PA1. Ketika sensor hujan mendeteksi curah hujan tinggi atau permukaan sensor terkena air sehingga menghasilkan logika HIGH, STM32 menganggap kondisi tersebut berpotensi memicu longsor. Dalam kondisi ini LED merah dan buzzer akan diaktifkan, sedangkan LCD menampilkan pesan "BAHAYA!" dan "Hujan Deras". Dengan demikian, sistem dapat memberikan peringatan dini ketika terjadi faktor lingkungan yang berpotensi menyebabkan ketidakstabilan lereng.

Jika sensor getaran dan sensor hujan berada dalam kondisi tidak aktif, maka sistem akan membaca nilai kemiringan lereng melalui sensor tilt pada pin PA2 menggunakan fasilitas Analog-to-Digital Converter (ADC) STM32. Nilai ADC yang diperoleh kemudian dikonversi menjadi nilai sudut kemiringan. Berdasarkan hasil konversi tersebut, sistem mengelompokkan kondisi lereng ke dalam tiga kategori, yaitu normal pada rentang 0°–10°, waspada pada rentang 10°–30°, dan bahaya longsor untuk kemiringan lebih dari 30°. Pada kondisi normal, LED hijau menyala dan buzzer tidak aktif. Pada kondisi waspada, LED kuning menyala serta buzzer berbunyi secara intermiten sebagai tanda peringatan awal. Sementara itu, jika kemiringan melebihi 30°, sistem mengaktifkan LED merah dan buzzer serta menampilkan pesan "BAHAYA!" dan "LONGSOR" pada LCD sebagai indikasi kondisi kritis.

Selain fungsi pemantauan otomatis, sistem juga dilengkapi dengan tombol darurat (Emergency Button) yang terhubung pada pin PA3 dan memanfaatkan fitur interrupt eksternal (EXTI3). Ketika tombol ditekan, STM32 segera menghentikan proses pemantauan normal dan langsung menjalankan prosedur darurat dengan mengaktifkan LED merah, buzzer, serta menampilkan pesan "EMERGENCY" dan "WARNING" pada LCD. Penggunaan interrupt memungkinkan respons sistem terjadi secara cepat tanpa harus menunggu siklus pembacaan sensor berikutnya. Seluruh proses pembacaan sensor, pengolahan data, dan pengendalian indikator dilakukan secara berulang (real-time monitoring) sehingga kondisi lingkungan dapat dipantau secara terus-menerus dan informasi peringatan dapat diberikan sesegera mungkin ketika terdeteksi potensi longsor.

7. Video Simulasi [kembali]

  • Video Simulasi

  • Video Demo


8. Kesimpulan dan Saran [kembali]

   8.1  Kesimpulan

1. Prototype sistem peringatan dini longsor berbasis mikrokontroler STM32F103 telah berhasil dirancang dan dibangun, mencakup proses inisialisasi perangkat, pembacaan sensor, pengambilan keputusan, hingga keluaran indikator.

2.    Sistem berhasil membaca dan mengolah tiga parameter utama penyebab longsor, yaitu getaran tanah (sensor getaran digital), curah hujan (sensor hujan digital), dan kemiringan lereng dalam bentuk sudut Tilt, Pitch, dan Roll (sensor ADXL345).

3.  Sistem peringatan bertingkat berupa kombinasi warna LED (hijau, kuning, merah) dan pola bunyi buzzer yang berbeda untuk setiap tingkat bahaya (normal, waspada, bahaya) telah berhasil diimplementasikan, dilengkapi tampilan status dan nilai sudut kemiringan secara real-time pada LCD 16x2.

4.  Prototype ini dapat digunakan sebagai media edukasi kebencanaan yang sederhana dan interaktif untuk memperkenalkan konsep serta parameter sistem peringatan dini longsor kepada masyarakat, khususnya di kawasan rawan longsor seperti Batu Busuk, Padang.

8.2 Saran

Untuk pengembangan lebih lanjut dari sistem ini, disarankan beberapa hal berikut:

  1.      Perlu dilakukan kalibrasi lebih lanjut terhadap sensor ADXL345 dan penentuan ulang nilai ambang batas (threshold) sudut kemiringan berdasarkan data karakteristik lereng di lokasi Batu Busuk secara langsung.
  2.   Penambahan modul komunikasi nirkabel (seperti GSM/SMS Gateway atau IoT melalui WiFi/LoRa) disarankan agar peringatan dapat diteruskan ke pihak terkait atau masyarakat secara jarak jauh, tidak hanya pada perangkat itu sendiri.
  3.         Diperlukan casing atau pelindung tahan cuaca (waterproof enclosure) agar prototype dapat diuji dan dioperasikan secara langsung di lapangan, mengingat kondisi cuaca di kawasan Batu Busuk yang sering hujan.
  4.     Penambahan sumber daya cadangan (baterai/solar panel) disarankan agar sistem tetap dapat beroperasi saat terjadi pemadaman listrik.


9. Download File [kembali]



Komentar

Postingan populer dari blog ini

kuliah