#include <stdio.h>
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <math.h>
#include "esp_adc/adc_oneshot.h"
#define I2C_MASTER_SCL_IO 22
#define I2C_MASTER_SDA_IO 21
#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_FREQ_HZ 100000
#define MPU6050_ADDR 0x68
#define MPU6050_PWR_MGMT_1 0x6B
#define MPU6050_ACCEL_XOUT_H 0x3B
#define MPU6050_GYRO_XOUT_H 0x43
#define SOIL_MOISTURE_PIN ADC_CHANNEL_6 // GPIO34
#define LED_PIN GPIO_NUM_12
#define BUZZER_PIN GPIO_NUM_14
#define RAIN_BUTTON_PIN GPIO_NUM_27
#define RAIN_DROP_THRESHOLD 10
#define INCREASE_THRESHOLD_RAIN 3.0f
#define INCREASE_THRESHOLD_DRY 5.0f
#define SOIL_MOISTURE_SMOOTHING_WINDOW 5
#define RAIN_BUTTON_DEBOUNCE_MS 50
#define ACCEL_CHANGE_THRESHOLD 0.3f
#define ROTATION_THRESHOLD 30.0f // deg/sec
float soil_moisture_buffer[SOIL_MOISTURE_SMOOTHING_WINDOW] = {0};
int soil_moisture_index = 0;
float soil_moisture_sum = 0;
float smooth_soil_moisture(float new_value) {
soil_moisture_sum -= soil_moisture_buffer[soil_moisture_index];
soil_moisture_buffer[soil_moisture_index] = new_value;
soil_moisture_sum += new_value;
soil_moisture_index = (soil_moisture_index + 1) % SOIL_MOISTURE_SMOOTHING_WINDOW;
return soil_moisture_sum / SOIL_MOISTURE_SMOOTHING_WINDOW;
}
void i2c_master_init() {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_param_config(I2C_MASTER_NUM, &conf);
i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);
}
esp_err_t mpu6050_write_byte(uint8_t reg, uint8_t data) {
uint8_t write_buf[2] = {reg, data};
return i2c_master_write_to_device(I2C_MASTER_NUM, MPU6050_ADDR, write_buf, 2, 1000 / portTICK_PERIOD_MS);
}
void mpu6050_init() {
mpu6050_write_byte(MPU6050_PWR_MGMT_1, 0x00);
}
esp_err_t read_mpu6050_data(int16_t *ax, int16_t *ay, int16_t *az, int16_t *gx, int16_t *gy, int16_t *gz) {
uint8_t data[14];
esp_err_t err = i2c_master_write_read_device(I2C_MASTER_NUM, MPU6050_ADDR,
(uint8_t[]){MPU6050_ACCEL_XOUT_H}, 1,
data, 14, 1000 / portTICK_PERIOD_MS);
if (err != ESP_OK) return err;
*ax = (data[0] << 8) | data[1];
*ay = (data[2] << 8) | data[3];
*az = (data[4] << 8) | data[5];
*gx = (data[8] << 8) | data[9];
*gy = (data[10] << 8) | data[11];
*gz = (data[12] << 8) | data[13];
return ESP_OK;
}
void init_gpio() {
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << LED_PIN) | (1ULL << BUZZER_PIN),
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&io_conf);
gpio_set_level(LED_PIN, 0);
gpio_set_level(BUZZER_PIN, 0);
gpio_config_t btn_conf = {
.pin_bit_mask = (1ULL << RAIN_BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
};
gpio_config(&btn_conf);
}
void alert_signal() {
gpio_set_level(LED_PIN, 1);
gpio_set_level(BUZZER_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(300));
gpio_set_level(LED_PIN, 0);
gpio_set_level(BUZZER_PIN, 0);
}
int debounce_button(int pin) {
int stable = gpio_get_level(pin);
for (int i = 0; i < RAIN_BUTTON_DEBOUNCE_MS / 10; i++) {
vTaskDelay(pdMS_TO_TICKS(10));
int current = gpio_get_level(pin);
if (current != stable) {
stable = current;
i = 0;
}
}
return stable;
}
void app_main(void) {
i2c_master_init();
mpu6050_init();
init_gpio();
adc_oneshot_unit_init_cfg_t adc_config = { .unit_id = ADC_UNIT_1 };
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_new_unit(&adc_config, &adc1_handle);
adc_oneshot_chan_cfg_t chan_cfg = {
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_DEFAULT
};
adc_oneshot_config_channel(adc1_handle, SOIL_MOISTURE_PIN, &chan_cfg);
int raw;
adc_oneshot_read(adc1_handle, SOIL_MOISTURE_PIN, &raw);
float last_soil = (float)raw / 4095.0f * 100.0f;
for (int i = 0; i < SOIL_MOISTURE_SMOOTHING_WINDOW; i++) soil_moisture_buffer[i] = last_soil;
soil_moisture_sum = last_soil * SOIL_MOISTURE_SMOOTHING_WINDOW;
int rain_drops = 0;
int last_button_state = 1;
float prev_ax = 0, prev_ay = 0, prev_az = 0;
while (1) {
int16_t ax, ay, az, gx, gy, gz;
float ax_g = 0, ay_g = 0, az_g = 0;
float gx_d = 0, gy_d = 0, gz_d = 0;
if (read_mpu6050_data(&ax, &ay, &az, &gx, &gy, &gz) == ESP_OK) {
ax_g = ax / 16384.0f;
ay_g = ay / 16384.0f;
az_g = az / 16384.0f;
gx_d = gx / 131.0f; // deg/sec
gy_d = gy / 131.0f;
gz_d = gz / 131.0f;
bool sudden_accel = fabsf(ax_g - prev_ax) > ACCEL_CHANGE_THRESHOLD ||
fabsf(ay_g - prev_ay) > ACCEL_CHANGE_THRESHOLD ||
fabsf(az_g - prev_az) > ACCEL_CHANGE_THRESHOLD;
bool sudden_rotation = fabsf(gx_d) > ROTATION_THRESHOLD ||
fabsf(gy_d) > ROTATION_THRESHOLD ||
fabsf(gz_d) > ROTATION_THRESHOLD;
if (sudden_accel || sudden_rotation) {
printf("⚠️ Motion or Rotation Detected! Buzzer ON\n");
gpio_set_level(BUZZER_PIN, 1);
} else {
gpio_set_level(BUZZER_PIN, 0);
}
prev_ax = ax_g;
prev_ay = ay_g;
prev_az = az_g;
}
// Soil moisture read
adc_oneshot_read(adc1_handle, SOIL_MOISTURE_PIN, &raw);
float soil_percent = (float)raw / 4095.0f * 100.0f;
float smooth_soil = smooth_soil_moisture(soil_percent);
float delta_soil = smooth_soil - last_soil;
printf("Soil:%.2f,ΔSoil:%.2f \nAx:%.2f,Ay:%.2f,Az:%.2f \nGx:%.2f,Gy:%.2f,Gz:%.2f\n",
smooth_soil, delta_soil, ax_g, ay_g, az_g, gx_d, gy_d, gz_d);
int button_state = debounce_button(RAIN_BUTTON_PIN);
if (last_button_state == 1 && button_state == 0) {
rain_drops++;
printf("Rain Drop Detected! Count = %d\n", rain_drops);
gpio_set_level(LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(LED_PIN, 0);
}
last_button_state = button_state;
float threshold = (rain_drops > RAIN_DROP_THRESHOLD) ? INCREASE_THRESHOLD_RAIN : INCREASE_THRESHOLD_DRY;
if (delta_soil > threshold) {
printf("LANDSLIDE ALERT! ΔSoil=%.2f%% > %.2f%%\n", delta_soil, threshold);
alert_signal();
}
last_soil = smooth_soil;
vTaskDelay(pdMS_TO_TICKS(1000));
}
}