Flowchart
Flowchart
Listing Program
1. main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body - EWS Banjir Bandang (I2C Migrated)
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h> // Diperlukan untuk fungsi sprintf (formatting teks LCD)
#include "i2c-lcd.h" // Driver LCD I2C berbasis HAL
/* USER CODE END Includes */zyt
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim2;
/* USER CODE BEGIN PV */
// Variabel Evaluasi Matriks Risiko
uint8_t risk_rain = 0;
uint8_t risk_distance = 0;
uint8_t risk_flow = 0;
uint8_t total_risk = 0;
uint8_t last_total_risk = 99; // Flag untuk deteksi perubahan status (anti-flicker LCD)
// Variabel Pengukuran Sensor
float distance = 0;
volatile uint32_t pulse_count = 0;
float water_flow_rate = 0.0; // Hasil kalkulasi debit dalam Liter/menit (L/min)
// Variabel Manajemen Waktu Non-Blocking (Menggantikan HAL_Delay agar tidak hang)
uint32_t last_sensor_time = 0;
uint32_t last_flow_time = 0;
// Buffer penampung teks LCD
char lcd_buffer[16];
// Fungsi delay mikrodetik manual menggunakan Timer 2 untuk Trigger Ultrasonic
void delay_us(uint32_t us) {
__HAL_TIM_SET_COUNTER(&htim2, 0);
while (__HAL_TIM_GET_COUNTER(&htim2) < us);
}
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);
static void MX_I2C1_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim2); // Menghidupkan Timer2 pembaca jarak
LCD_Init(); // Inisialisasi awal layar LCD via I2C
LCD_SendString("EWS SYSTEM READY");
HAL_Delay(1500);
LCD_Cmd(0x01); // Bersihkan Layar awal
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
uint32_t current_time = HAL_GetTick();
// =========================================================================
// 1. KALKULASI DEBIT WATER FLOW DARI PULSA CLOCK PROTEUS (TIAP 1 DETIK)
// =========================================================================
if (current_time - last_flow_time >= 1000) {
uint32_t frequency = pulse_count; // Frekuensi = Jumlah pulsa dalam 1 detik (Hz)
pulse_count = 0; // Reset counter untuk detik berikutnya
last_flow_time = current_time;
/* Menggunakan Rumus Sensor Aliran Air Populer (YF-S201): F = 7.5 * Q
* Di mana F adalah Frekuensi (Hz) dan Q adalah Debit Air (L/min)
* Maka: Q = F / 5.5
*/
if (frequency > 0) {
water_flow_rate = (float)frequency / 5.5;
} else {
water_flow_rate = 0.0;
}
}
// =========================================================================
// 2. AMBIL DATA SENSOR & UPDATE OUTPUT SECARA BERKALA (TIAP 200 ms)
// =========================================================================
if (current_time - last_sensor_time >= 200) {
last_sensor_time = current_time;
// A. BACA SENSOR HUJAN DIGITAL (PA0)
if (HAL_GPIO_ReadPin(RAIN_SENSOR_GPIO_Port, RAIN_SENSOR_Pin) == GPIO_PIN_RESET) {
risk_rain = 1; // Terdeteksi Hujan
} else {
risk_rain = 0; // Kondisi Kering
}
// B. BACA SENSOR JARAK ULTRASONIC DENGAN TIMEOUT (PA1 & PA2)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // Trigger HIGH
delay_us(10);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // Trigger LOW
uint32_t timeout1 = 0;
while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_RESET) {
timeout1++;
if(timeout1 > 5000) break;
}
__HAL_TIM_SET_COUNTER(&htim2, 0); // Mulai hitung durasi
uint32_t timeout2 = 0;
while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_SET) {
timeout2++;
if(timeout2 > 5000) break;
}
uint32_t duration = __HAL_TIM_GET_COUNTER(&htim2);
distance = duration * 0.034 / 2; // Konversi waktu ke jarak (cm)
// C. EVALUASI MATRIKS RISIKO INDIVIDU SENSOR
// 1. Batas Jarak Luapan Air Sungai
if (distance > 25.0 || distance <= 2.0) {
risk_distance = 0;
}
else if (distance >= 12.0) {
risk_distance = 1; // Ketinggian Sedang (Siaga)
}
else {
risk_distance = 2; // Kritis meluap (Bahaya)
}
// 2. Batas Kecepatan Debit Air (Water Flow)
if (water_flow_rate < 5.0) {
risk_flow = 0;
}
else if (water_flow_rate <= 30.0) {
risk_flow = 1; // Arus Sedang (Siaga)
}
else {
risk_flow = 2; // Arus Sangat Deras (Bahaya)
}
// Akumulasi Total Skor Risiko
total_risk = risk_rain + risk_distance + risk_flow;
// D. MANAJEMEN RESET LAYAR LCD (Anti-Flicker I2C)
if (total_risk != last_total_risk) {
LCD_Cmd(0x01);
last_total_risk = total_risk;
}
// Tampilkan Data Sensor di Baris 2 LCD
LCD_SetCursor(2, 1);
sprintf(lcd_buffer, "J:%2d Q:%2d R:%1d", (int)distance, (int)water_flow_rate, total_risk);
LCD_SendString(lcd_buffer);
// E. EKSEKUSI INDIKATOR OUTPUT (LED, BUZZER, & STATUS BARIS 1 LCD)
LCD_SetCursor(1, 1);
if (total_risk <= 1) {
/* ------ KONDISI 1: AMAN ------ */
HAL_GPIO_WritePin(GPIOB, LED_HIJAU_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, LED_KUNING_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, LED_MERAH_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, BUZZER_Pin, GPIO_PIN_RESET);
LCD_SendString("STATUS: AMAN ");
}
else if (total_risk == 2 || total_risk == 3) {
/* ------ KONDISI 2: SIAGA ------ */
HAL_GPIO_WritePin(GPIOB, LED_HIJAU_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, LED_KUNING_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, LED_MERAH_Pin, GPIO_PIN_RESET);
// Membuat Buzzer berbunyi intermiten pelan (Toggle tiap loop 200ms)
HAL_GPIO_TogglePin(GPIOB, BUZZER_Pin);
LCD_SendString("STATUS: SIAGA ");
}
else if (total_risk >= 4) {
/* ------ KONDISI 3: BAHAYA ------ */
HAL_GPIO_WritePin(GPIOB, LED_HIJAU_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, LED_KUNING_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, LED_MERAH_Pin, GPIO_PIN_SET);
// Buzzer berbunyi konstan tanpa putus
HAL_GPIO_WritePin(GPIOB, BUZZER_Pin, GPIO_PIN_SET);
LCD_SendString("FLASH FLOOD!! ");
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
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_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
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();
}
}
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
// FIXED: Menggunakan Prescaler 72-1 karena clock sistem berjalan di 72MHz (HSE PLL)
htim2.Init.Prescaler = 72-1;
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();
}
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, LED_HIJAU_Pin|LED_KUNING_Pin|LED_MERAH_Pin|BUZZER_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : RAIN_SENSOR_Pin */
GPIO_InitStruct.Pin = RAIN_SENSOR_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(RAIN_SENSOR_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PA1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : PA2 */
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : FLOW_PIN_Pin */
GPIO_InitStruct.Pin = FLOW_PIN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(FLOW_PIN_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : LED_HIJAU_Pin LED_KUNING_Pin LED_MERAH_Pin BUZZER_Pin */
GPIO_InitStruct.Pin = LED_HIJAU_Pin|LED_KUNING_Pin|LED_MERAH_Pin|BUZZER_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);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
/* USER CODE BEGIN 4 */
// Callback EXTI0: Menangkap pulsa dari CLOCK Generator Proteus secara real-time
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == FLOW_PIN_Pin) { // Menggunakan alias Pin Name yang sesuai dengan .ioc kamu
pulse_count++;
}
}
/* USER CODE END 4 */
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
while(1)
{
}
/* USER CODE END Error_Handler_Debug */
}
Library
2.l2c_lcd.c
#include "main.h"
#include "i2c-lcd.h"
extern I2C_HandleTypeDef hi2c1; // Mengambil handle I2C dari main.c
#define SLAVE_ADDRESS_LCD 0x4E // Alamat I2C default PCF8574 (0x27 << 1)
void LCD_Cmd(char cmd) {
char data_u, data_l;
uint8_t data_t[4];
data_u = (cmd & 0xf0);
data_l = ((cmd << 4) & 0xf0);
data_t[0] = data_u | 0x0C; // en=1, rs=0
data_t[1] = data_u | 0x08; // en=0, rs=0
data_t[2] = data_l | 0x0C; // en=1, rs=0
data_t[3] = data_l | 0x08; // en=0, rs=0
HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t *)data_t, 4, 100);
}
void LCD_Data(char data) {
char data_u, data_l;
uint8_t data_t[4];
data_u = (data & 0xf0);
data_l = ((data << 4) & 0xf0);
data_t[0] = data_u | 0x0D; // en=1, rs=1
data_t[1] = data_u | 0x09; // en=0, rs=1
data_t[2] = data_l | 0x0D; // en=1, rs=1
data_t[3] = data_l | 0x09; // en=0, rs=1
HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t *)data_t, 4, 100);
}
void LCD_Init(void) {
HAL_Delay(50);
LCD_Cmd(0x30);
HAL_Delay(5);
LCD_Cmd(0x30);
HAL_Delay(1);
LCD_Cmd(0x32);
HAL_Delay(10);
LCD_Cmd(0x28); // Mode 4-bit, 2 Baris
HAL_Delay(1);
LCD_Cmd(0x0C); // Display ON, Cursor OFF
HAL_Delay(1);
LCD_Cmd(0x06); // Auto-increment kursor
HAL_Delay(1);
LCD_Cmd(0x01); // Clear Screen
HAL_Delay(2);
}
void LCD_SendString(char *str) {
while (*str) LCD_Data(*str++);
}
void LCD_SetCursor(uint8_t row, uint8_t col) {
uint8_t address = (row == 1) ? (0x80 + col - 1) : (0xC0 + col - 1);
LCD_Cmd(address);
}
3.i2c_lcd.h
#ifndef INC_I2C_LCD_H_
#define INC_I2C_LCD_H_
#include "main.h"
void LCD_Init(void);
void LCD_Cmd(char cmd);
void LCD_Data(char data);
void LCD_SendString(char *str);
void LCD_SetCursor(uint8_t row, uint8_t col);
#endif /* INC_I2C_LCD_H_ */

Komentar
Posting Komentar