#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_sleep.h"
#include "esp_system.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include "esp_timer.h"
#include "esp_pm.h"
// Pin definitions
#define LED_PINR GPIO_NUM_2 // GPIO2 for Red LED
#define LED_PINB GPIO_NUM_0 // GPIO0 for Blue LED
#define LED_PING GPIO_NUM_4 // GPIO4 for Green LED
#define LED_PIN GPIO_NUM_23 //cyan as humidifer
#define BUTTON_PIN GPIO_NUM_16 // Source Button for MODE CHANGE connected to GPIO 16
#define MANUAL_BUTTON_PIN GPIO_NUM_15 //manual button for misting
// Timing constants
const int64_t debounce_time_ms = 20; // 20ms debounce time
const int64_t long_press_time_ms = 4000; // 4 seconds for long press // Time to turn off LED when pressed for 4 seconds // in fututr preset values
const int64_t restart_press_time_ms = 10000;
const int64_t ultra_press_time_ms = 20000; //unusually button pressed for high time of 20secs
const int64_t blink_interval_ms = 500; // Blink interval for LED
int64_t last_blink_time = 0; // Store the last blink toggle time
//RTC_DATA_ATTR int sleeping_var=0;
// Define modes
typedef enum {
MODE_DEEPSLEEP = 0,
MODE_MANUAL,
MODE_BT
} mode_t;
static int transition =0;
static int same_mode=0;
static int returned_manual=0;
static esp_adc_cal_characteristics_t adc1_chars;
//static
float water_sens_global;
static int humidifier_state = 0;
static unsigned long last_water_check_time = 0;
volatile mode_t current_mode = MODE_DEEPSLEEP;
static int short_press=0;
static int long_press=0;
static int restart=0;
static int ultra_press=0;
bool led_blinking = false; // Track if LED is in blinking mode
static int source_led_on = 0; // Track if LED is on or off
// Global flag for manual button press
volatile bool manual_button_pressed = false;
// ISR to handle the second (manual) button press
void IRAM_ATTR manual_button_isr_handler(void* arg) {
static uint32_t last_manual_isr_time = 0;
uint32_t manual_isr_time = xTaskGetTickCountFromISR();
// Debounce the button press (200ms)
if (manual_isr_time - last_manual_isr_time > pdMS_TO_TICKS(200)) {
manual_button_pressed = true; // Mark the manual button as pressed
}
last_manual_isr_time = manual_isr_time;
}
// Configure button GPIO with ISR
void configure_button() {
// Configure the button GPIO
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = 1, // Enable pull-up resistor
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE // No interrupts
};
gpio_config(&io_conf);
// Configure second button (MANUAL_BUTTON_PIN)
io_conf.intr_type = GPIO_INTR_POSEDGE; // Interrupt on rising edge (button press)
io_conf.pin_bit_mask = (1ULL << MANUAL_BUTTON_PIN); // Set pin for the second button
io_conf.mode = GPIO_MODE_INPUT; // Set as input
io_conf.pull_up_en = 1; // Enable pull-up resistor
io_conf.pull_down_en = 0;
gpio_config(&io_conf);
// Install ISR service and add ISR handler for both buttons
gpio_install_isr_service(0);
gpio_isr_handler_add(MANUAL_BUTTON_PIN, manual_button_isr_handler, NULL); // Second button ISR
}
void configure_gpio(){
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(LED_PIN, 0); // Start with the LED off
gpio_set_level(LED_PINR, 0);
gpio_set_level(LED_PINB, 0);
gpio_set_level(LED_PING, 0);
gpio_set_direction(LED_PINR, GPIO_MODE_OUTPUT);
gpio_set_direction(LED_PINB, GPIO_MODE_OUTPUT);
gpio_set_direction(LED_PING, GPIO_MODE_OUTPUT);
}
void configure_adc(){
// ADC configuration for potentiometer and water level sensors
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars);
adc1_config_width(ADC_WIDTH_BIT_DEFAULT);
adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11); // Potentiometer (GPIO32)
adc1_config_channel_atten(ADC1_CHANNEL_5, ADC_ATTEN_DB_0); // Water level sensor (GPIO33)
}
void check_manual_button_press() {
// Check if the manual button was pressed
if (manual_button_pressed) {
printf("2nd button (Manual Button) pressed!\n");
manual_button_pressed = false; // Reset the flag after handling
}
}
void manual_button_mist(){
//printf("hi");
humidifier_state = !humidifier_state; // Toggle humidifier state
gpio_set_level(LED_PIN, humidifier_state);
printf("Humidifier is now: %s\n", humidifier_state ? "ON" : "OFF");
}
void check_water_level() {
float water_sens = adc1_get_raw(ADC1_CHANNEL_5); // Read water level
water_sens_global = water_sens;
float Height_Level = (water_sens * 5.00) /4095.00 ; // in cm
printf("Water Level Routine Check Value: %f cm \n", Height_Level );
// Check if water level is below the threshold
if (Height_Level < 0.5 && humidifier_state) {
humidifier_state = 0; // Turn off humidifier
gpio_set_level(LED_PIN, humidifier_state);
printf("Water level is low. Humidifier turned OFF.\n");
}
}
// Enter deep sleep mode
void enter_deep_sleep() {
//sleeping_var=1;
printf("Entering deep sleep...\n");
gpio_set_level(LED_PIN, 0); // Turn off LED before sleeping
esp_sleep_enable_ext0_wakeup(BUTTON_PIN, 0); //Wake up on button press (falling edge)
gpio_set_level(LED_PINR, 0);
gpio_set_level(LED_PINB, 0);
gpio_set_level(LED_PING, 0);
esp_deep_sleep_start(); // Enter deep sleep
}
// Manual mode logic
void manual_mode() {
gpio_set_level(LED_PINR, 1); // Red LED for manual mode
gpio_set_level(LED_PINB, 0);
gpio_set_level(LED_PING, 0);
printf("Manual Mode\n");
source_led_on = 1;
int button_level = 1; // Button is high when not pressed (assuming pull-up)
int last_button_level = 1;
int64_t press_start_time = 0;
bool button_pressed = false;
while (1) {
//printf("Inside Manual Mode\n");
//int water_sens = adc1_get_raw(ADC1_CHANNEL_5); // linear pot
// Check if the manual button was pressed
if (manual_button_pressed) {
printf("2nd button (Manual Button) pressed!\n");
manual_button_pressed = false; // Reset the flag after handling
//// here just after button pressen and check if water level we need to turen pn 5v buck on with delay
//inital checking only once
float water_sens = adc1_get_raw(ADC1_CHANNEL_5); // Read water level
float Height_Level = (water_sens * 5.00) /4095.00 ; // in cm
water_sens_global =water_sens;
printf("water level after pressing button: %f cm\n",Height_Level);
if(Height_Level >= 0 && Height_Level <= 0.5 ){
printf("Water Critically Low Cannot turn on Humidifer \n");
}
if(Height_Level>0.5){
//routine_check =1;
manual_button_mist();
}
}
//checking routine block only just before drain time
if( humidifier_state == 1){
//printf("started \n");
float Height_Level = (water_sens_global * 5.00) /4095.00 ;
float check_time = (0.22727*Height_Level ) * 0.75 ; // actual time in hr , 0.75 to check a bit before the actual drain time
float sec_check = (check_time*15.00)/1.1418 ; // scaled time for now
unsigned long current_time = xTaskGetTickCount() * portTICK_PERIOD_MS;
if (current_time - last_water_check_time >= 1000*sec_check) { // assumed seconds interval
printf("Checking in intevals of : %f secs , %f hr \n",sec_check,check_time);
check_water_level(); // Check water level
last_water_check_time = current_time; // Update last check time
}
}
button_level = gpio_get_level(BUTTON_PIN);
if (button_level == 0 && last_button_level == 1) { // Button is pressed (high to low transition)
press_start_time = esp_timer_get_time(); // Record press start time in microseconds
button_pressed = true;
} else if (button_level == 1 && last_button_level == 0) { // Button is released (low to high transition)
if (button_pressed) {
int64_t press_duration_ms = (esp_timer_get_time() - press_start_time) / 1000; // Convert to milliseconds
if (press_duration_ms >= ultra_press_time_ms){
printf("Ultra press detected (duration: %lld ms)\n", press_duration_ms);
} else if(press_duration_ms >= restart_press_time_ms){
printf("RESTART detected (duration: %lld ms)\n", press_duration_ms);
restart=1;
} else if (press_duration_ms >= long_press_time_ms) {
printf("Long press detected (duration: %lld ms)\n", press_duration_ms);
long_press=1;
} else if (press_duration_ms >= debounce_time_ms) {
printf("Short press detected (duration: %lld ms)\n", press_duration_ms);
short_press = 1;
}
led_blinking = false;
button_pressed = false; // Reset button pressed state
}
}
// Check if button remains pressed for 4 seconds to turn off LED
if (button_pressed) {
int64_t current_press_duration_ms = (esp_timer_get_time() - press_start_time) / 1000;
if (current_press_duration_ms >= ultra_press_time_ms){
printf("Ultra press detected (duration: %lld ms)\n", current_press_duration_ms);
ultra_press=1;
returned_manual =1;
return;
} else if ( current_press_duration_ms >= restart_press_time_ms && led_blinking ){
gpio_set_level(LED_PINR, 0); // Red LED for manual mode
gpio_set_level(LED_PINB, 0);
gpio_set_level(LED_PING, 0);
source_led_on=0;
led_blinking= false;
printf("SOURCE LED turned of at 10 secs \n");
} else if (current_press_duration_ms >= long_press_time_ms && !led_blinking && current_press_duration_ms < restart_press_time_ms) {
led_blinking = true; // Enable blinking mode
last_blink_time = esp_timer_get_time() / 1000; // Initialize last blink time
printf("LED started blinking after 4 seconds of button press\n");
}
}
// Handle LED blinking
if (led_blinking) {
int64_t current_time_ms = esp_timer_get_time() / 1000;
if (current_time_ms - last_blink_time >= blink_interval_ms ) {
source_led_on = !source_led_on; // Toggle LED state
gpio_set_level(LED_PINR, source_led_on); // Red LED for manual mode
gpio_set_level(LED_PINB, 0);
gpio_set_level(LED_PING, 0); // Update LED based on toggled state
last_blink_time = current_time_ms; // Update last blink time
}
}
last_button_level = button_level; // Update last button state
vTaskDelay(pdMS_TO_TICKS(10));
if(restart){
printf("going out of manual after restart needed \n");
returned_manual =1;
return;
}
// Check if the Source button has been pressed again to exit and go BT mode
if (short_press) {
short_press = 1; // Reset the press flag
printf("Button pressed inside Manual Mode, exiting...\n");
// Update the mode to BT mode before exiting
//transition=1;
// Exit the manual mode loop and return to main loop to handle mode change
returned_manual =1;
return;
}
if(long_press){
returned_manual =1;
return;
}
// Add any other logic for manual mode operation here
// e.g., turning the humidifier on/off or checking sensors
vTaskDelay(pdMS_TO_TICKS(100)); // Small delay to avoid busy waiting
}
// Indicate that Manual Mode has exited and BT Mode can be entered
printf("Exiting Manual Mode, switching to BT Mode...\n");
}
// Bluetooth mode logic (replace with actual BT setup)
void bt_mode() {
//gpio_set_level(LED_PIN, 1);
gpio_set_level(LED_PINR, 0);
gpio_set_level(LED_PINB, 1); // Blue LED for BT mode
gpio_set_level(LED_PING, 0);
printf("Bluetooth Mode\n");
}
// Cycle modes based on button press
void cycle_modes() {
// Update the mode
current_mode = (current_mode + 1) % 3;
// Perform actions based on the current mode
switch (current_mode) {
case MODE_MANUAL:
manual_mode();
break;
case MODE_BT:
bt_mode();
break;
case MODE_DEEPSLEEP:
//sleeping_var =1;
//vTaskDelay(pdMS_TO_TICKS(10));
enter_deep_sleep(); // Go back to deep sleep
break;
default:
break;
}
}
void app_main() {
// Configure the button and LED
configure_button();
configure_gpio();
int button_level = 1; // Button is high when not pressed (assuming pull-up)
int last_button_level = 1;
int64_t press_start_time = 0;
bool button_pressed = false;
printf("outside main while \n");
vTaskDelay(pdMS_TO_TICKS(10));
//printf("deep sleep = %d \n ",sleeping_var);
// Main loop
while (1) {
// Check if the button was pressed
// Read the button level
button_level = gpio_get_level(BUTTON_PIN);
if (returned_manual){
vTaskDelay(pdMS_TO_TICKS(10));
printf("returned from Manual and entered while of main \n");
vTaskDelay(pdMS_TO_TICKS(10));
returned_manual =0;
if(ultra_press){
printf("deep sleeping coz of ultra press \n");
ultra_press=0;
gpio_set_level(LED_PINR, 0); // Red LED for manual mode
gpio_set_level(LED_PINB, 0);
gpio_set_level(LED_PING, 0);
//enter_deep_sleep(); // Go back to deep sleep
esp_restart();
}
if(restart) {
printf("restarted\n");
vTaskDelay(pdMS_TO_TICKS(10)); // Give time for print
humidifier_state = 0;
last_water_check_time = 0;
short_press=0;
long_press=0;
//transition=0;
restart=0;
//same_mode=0;
//short_press=1;
led_blinking = false; // Track if LED is in blinking mode
//source_led_on = 0; // Track if LED is on or off
esp_restart();
}
vTaskDelay(pdMS_TO_TICKS(10));
button_level = 1;
} else if (button_level == 0 && last_button_level == 1 ) { // Button is pressed (high to low transition)
press_start_time = esp_timer_get_time(); // Record press start time in microseconds
button_pressed = true;
} else if (button_level == 1 && last_button_level == 0) { // Button is released (low to high transition)
if (button_pressed) {
int64_t press_duration_ms = (esp_timer_get_time() - press_start_time) / 1000; // Convert to milliseconds
if (press_duration_ms >= long_press_time_ms) {
printf("Long press detected before AWAKE (duration: %lld ms)\n", press_duration_ms);
//long_press=1;
} else if (press_duration_ms >= debounce_time_ms) {
printf("Short press detected (duration: %lld ms)\n", press_duration_ms);
short_press = 1;
}
button_pressed = false; // Reset button pressed state
}
}
last_button_level = button_level; // Update last button state
//vTaskDelay(pdMS_TO_TICKS(10)); // Poll the button every 10ms
/*
if(same_mode){
printf("restarted and came back to same mode");
same_mode=0;
//transition=1;
}
if(transition==1){
transition=0;
cycle_modes();
}
*/
if (short_press) {
short_press = 0; // Reset the press flag
cycle_modes(); // Cycle through the modes
}
if(long_press){
printf("deep sleeping coz of long press \n");
long_press=0;
enter_deep_sleep(); // Go back to deep sleep
}
check_manual_button_press();
vTaskDelay(pdMS_TO_TICKS(100)); // Small delay to avoid rapid polling
}
}