/*
# Sound Level Monitor with Notification Alerts

This project uses an ESP32 and a sound sensor to monitor ambient noise levels.
It calculates a 15-second weighted average, displays real-time status in the Blynk app,
and sends a Make.com webhook notification if the noise exceeds a predefined threshold.

## Hardware Requirements
- ESP32 development board  
- Analog sound sensor  
- Power supply for ESP32  
- Smartphone with Blynk app installed

## Pin Connections
- Sound Sensor: GPIO 33 (Analog input)

## Features
- Reads ambient sound levels every second  
- Normalizes sound data to a range of 15–50  
- Calculates a weighted average over 15 seconds  
- Displays sound level average (V4) and status (LOW, MED, HIGH) on Blynk (V7)  
- Sends a webhook alert via Make.com if average sound exceeds the threshold  
- Detects and handles `millis()` overflow to ensure stability  
- Serial output for debugging and real-time feedback

## Video
- https://youtu.be/qRPGj36vkL0?t=125

## Credits
- Theodore_Dai_Maman #211541594  
- Omer_Dan #322952466  
- Amit_Kaminsky #207487661  

## Setup Instructions
1. Install required libraries in Arduino IDE:  
   - Blynk  
   - WiFi  
   - HTTPClient  

2. Update the following in the code:  
   - `ssid` and `password` with your WiFi credentials  
   - `BLYNK_AUTH_TOKEN` with your Blynk auth token  
   - `makeWebhookUrl` with your Make.com webhook URL  

3. Upload the code to your ESP32  

## Notes
- The average sound level is updated every 15 seconds  
- The `SOUND_THRESHOLD` (default: 25) determines when to send an alert  
- The normalized range can be fine-tuned by adjusting `SOUND_MIN` and `SOUND_MAX`  
- Useful for detecting disturbances, monitoring quiet zones, or classroom noise  
*/

// First, define Blynk credentials BEFORE including Blynk libraries
#define BLYNK_TEMPLATE_ID           "Enter Template ID"
#define BLYNK_TEMPLATE_NAME         "Enter Template Name"
#define BLYNK_AUTH_TOKEN            "Enter Auth Token"
#define BLYNK_PRINT Serial

// Then include the libraries
#include <WiFi.h>
#include <BlynkSimpleEsp32.h>
#include <HTTPClient.h>  // Add this include for HTTP requests

// WiFi credentials
const char* ssid = "Enter SSID";
const char* password = "Enter Password";

// Pin definitions
#define SOUND_PIN 33  // Analog pin for sound sensor

// Sound monitoring settings
#define SAMPLE_INTERVAL 1000   // Sample every 1 second (changed from 100ms)
#define SAMPLE_PERIOD 15000    // Calculate average over 15 seconds
#define SOUND_THRESHOLD 25     // Threshold for noise alert (in normalized range 0-50)
#define SOUND_MIN 0           // Minimum raw value from sensor
#define SOUND_MAX 4095        // Maximum raw value from sensor (12-bit ADC)

// Variables for sound monitoring
unsigned long lastSampleTime = 0;
unsigned long lastCalculationTime = 0;
unsigned long sampleCount = 0;
float totalSound = 0.0;    // Changed from unsigned long to float
float weightedAverage = 0.0;

// Make.com webhook URL for notifications
const char* makeWebhookUrl = "Enter Webhook URL";  // Replace with your Make.com webhook URL

// Function to normalize sound value to 15-50 range
float normalizeSoundValue(int rawValue) {
  if (rawValue >= SOUND_MAX) {
    return 50.0;  // Return maximum normalized value
  }
  if (rawValue <= SOUND_MIN) {
    return 15.0;   // Return minimum normalized value (changed from 0.0)
  }
  // Scale to 15-50 range (35 point spread instead of 50)
  return 15.0 + (float)(rawValue - SOUND_MIN) * 35.0 / (SOUND_MAX - SOUND_MIN);
}

void setup() {
  Serial.begin(115200);
  Serial.println("\nStarting Sound Level Monitor");
  
  // Connect to WiFi
  Serial.print("Connecting to WiFi");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi Connected!");

  // Connect to Blynk
  Serial.print("Connecting to Blynk...");
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, "");
  Serial.println("Connected!");
}

void loop() {
  Blynk.run();
  
  unsigned long currentTime = millis();
  
  // Add overflow protection for millis()
  if (currentTime < lastSampleTime) {  // millis() has overflowed
    lastSampleTime = currentTime;
    lastCalculationTime = currentTime;
  }
  
  // Sample sound level every SAMPLE_INTERVAL
  if (currentTime - lastSampleTime >= SAMPLE_INTERVAL) {
    int rawSoundValue = analogRead(SOUND_PIN);
    float normalizedValue = normalizeSoundValue(rawSoundValue);
    
    // Add bounds checking
    if (sampleCount < 1000) {  // Prevent excessive accumulation
      totalSound += normalizedValue;
      sampleCount++;
    }
    
    // Print raw and normalized values with clear indication when maxed out
    Serial.print("Raw: ");
    Serial.print(rawSoundValue);
    Serial.print(" | Normalized: ");
    Serial.print(normalizedValue);
    if (rawSoundValue >= SOUND_MAX) {
      Serial.print(" (MAX)");
    }
    Serial.print(" | Count: ");
    Serial.print(sampleCount);
    Serial.print(" | Total: ");
    Serial.println(totalSound);
    
    lastSampleTime = currentTime;
  }
  
  // Calculate weighted average every SAMPLE_PERIOD
  if (currentTime - lastCalculationTime >= SAMPLE_PERIOD) {
    if (sampleCount > 0) {
      weightedAverage = totalSound / sampleCount;
      
      // Send to Blynk
      if (Blynk.connected()) {
        // Send numeric value to V4
        Blynk.virtualWrite(V4, weightedAverage);
        
        // Send text status to V7
        if (weightedAverage <= 20) {
          Blynk.virtualWrite(V7, "LOW");
          Serial.println("Sound Level: LOW");
        } else if (weightedAverage <= 35) {
          Blynk.virtualWrite(V7, "MED");
          Serial.println("Sound Level: MED");
        } else {
          Blynk.virtualWrite(V7, "HIGH");
          Serial.println("Sound Level: HIGH");
        }
        
        Serial.print("\n=== 15-Second Average ===\n");
        Serial.print("Normalized average: ");
        Serial.println(weightedAverage);
        Serial.println("========================\n");
      }
      
      // Check threshold and send notification if exceeded
      if (weightedAverage > SOUND_THRESHOLD) {
        sendMakeNotification(weightedAverage);
      }
      
      // Reset counters
      totalSound = 0.0;
      sampleCount = 0;
    }
    lastCalculationTime = currentTime;
  }
  
  delay(10);
}

void sendMakeNotification(float soundLevel) {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(makeWebhookUrl);
    http.addHeader("Content-Type", "application/json");
    
    // Create JSON payload
    String payload = "{\"sound_level\":" + String(soundLevel) + "}";
    
    // Send POST request
    int httpResponseCode = http.POST(payload);
    
    if (httpResponseCode > 0) {
      Serial.print("Make.com notification sent. Response code: ");
      Serial.println(httpResponseCode);
    } else {
      Serial.print("Error sending notification. Error code: ");
      Serial.println(httpResponseCode);
    }
    
    http.end();
  }
} 