#include // Feature Configuration - Comment out to disable #define ENABLE_SENSORS #define ENABLE_DISPLAY #define ENABLE_WIFI #define ENABLE_NOTIFICATIONS #ifdef ENABLE_WIFI #include #include #endif #ifdef ENABLE_SENSORS #include #endif #ifdef ENABLE_DISPLAY #include #include #endif // WiFi credentials #ifdef ENABLE_WIFI const char* WIFI_SSID = "YOUR_WIFI_SSID"; const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD"; #endif // ntfy.sh topic const char* NTFY_TOPIC = "YOUR_NTFY_TOPIC"; // Pin definitions const int VIBRATION_PIN = 4; // OLED display settings #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 32 #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) #define SCREEN_ADDRESS 0x3C #ifdef ENABLE_DISPLAY Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); #endif // Light sensor #ifdef ENABLE_SENSORS BH1750 lightMeter; #endif // Thresholds const float LIGHT_ACTIVATION_THRESHOLD = 200.0; const float LIGHT_DEACTIVATION_THRESHOLD = 10.0; const unsigned long VIBRATION_STATE_CHANGE_INTERVAL = 5000; // 5 seconds // Device state bool isVibrationActive = false; bool isLightActive = false; bool isDeviceActive = false; // Time tracking for sensor states unsigned long vibrationLowStartTime = 0; unsigned long vibrationHighStartTime = 0; unsigned long lightHighStartTime = 0; unsigned long lightLowStartTime = 0; // OLED display data #ifdef ENABLE_DISPLAY String displayData[4]; int currentDisplayLine = 0; unsigned long lastDisplayScrollTime = 0; const unsigned long DISPLAY_SCROLL_INTERVAL = 2000; // 1000 = 1 second const int displayTextLines = 5; int displayDataLines = displayTextLines; #endif // Notification queue String queuedNotification = ""; String queuedTitle = ""; String queuedPriority = ""; void setup() { Serial.begin(9600); Serial.println("Setup started."); #if defined(ENABLE_SENSORS) || defined(ENABLE_DISPLAY) Wire.begin(); #endif #ifdef ENABLE_SENSORS // Initialize vibration sensor pin pinMode(VIBRATION_PIN, INPUT); // Initialize light sensor if (!lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) { Serial.println(F("Could not find a valid BH1750 sensor, check wiring!")); while (1) { } } #endif #ifdef ENABLE_DISPLAY // Initialize OLED display if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1306 allocation failed")); for (;;); } display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); display.println(F("Initializing...")); display.display(); #endif #ifdef ENABLE_WIFI // Connect to WiFi Serial.print("Connecting to "); Serial.println(WIFI_SSID); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); #endif #if defined(ENABLE_DISPLAY) && defined(ENABLE_WIFI) display.clearDisplay(); display.setCursor(0,0); display.println("WiFi Connected"); display.display(); delay(1000); #endif Serial.println("Setup finished."); } bool sendNotification(String message, String title, String priority); void updateDisplay(); void loop() { #ifdef ENABLE_NOTIFICATIONS // Try to send any queued notification if (queuedNotification != "") { if (sendNotification(queuedNotification, queuedTitle, queuedPriority)) { Serial.println("Successfully sent queued notification."); queuedNotification = ""; // Clear queue queuedTitle = ""; queuedPriority = ""; } else { // Failed, will retry on next loop } } #endif unsigned long currentTime = millis(); // Sensor readings #ifdef ENABLE_SENSORS bool currentVibrationState = (digitalRead(VIBRATION_PIN) == LOW); float currentLightLevel = lightMeter.readLightLevel(); #else bool currentVibrationState = false; float currentLightLevel = 0.0; #endif // Vibration state change logic if (currentVibrationState && !isVibrationActive) { // Vibration is active (LOW) if (vibrationLowStartTime == 0) { vibrationLowStartTime = currentTime; } if (currentTime - vibrationLowStartTime >= VIBRATION_STATE_CHANGE_INTERVAL) { isVibrationActive = true; } vibrationHighStartTime = 0; // Reset high timer } else if (!currentVibrationState && isVibrationActive) { // Vibration is inactive (HIGH) if (vibrationHighStartTime == 0) { vibrationHighStartTime = currentTime; } if (currentTime - vibrationHighStartTime >= VIBRATION_STATE_CHANGE_INTERVAL) { isVibrationActive = false; } vibrationLowStartTime = 0; // Reset low timer } else if (currentVibrationState) { vibrationHighStartTime = 0; } else { vibrationLowStartTime = 0; } // Light state change logic if (currentLightLevel >= LIGHT_ACTIVATION_THRESHOLD && !isLightActive) { if (lightHighStartTime == 0) { lightHighStartTime = currentTime; } if (currentTime - lightHighStartTime >= VIBRATION_STATE_CHANGE_INTERVAL) { isLightActive = true; } lightLowStartTime = 0; } else if (currentLightLevel < LIGHT_DEACTIVATION_THRESHOLD && isLightActive) { if (lightLowStartTime == 0) { lightLowStartTime = currentTime; } if (currentTime - lightLowStartTime >= VIBRATION_STATE_CHANGE_INTERVAL) { isLightActive = false; } lightHighStartTime = 0; } else if(currentLightLevel >= LIGHT_ACTIVATION_THRESHOLD) { lightLowStartTime = 0; } else { lightHighStartTime = 0; } // Overall device state bool previousDeviceState = isDeviceActive; isDeviceActive = isVibrationActive || isLightActive; if (isDeviceActive != previousDeviceState) { String message; String title; String priority; if (isDeviceActive) { message = "Washing machine cycle started."; title = "Machine START"; priority = "default"; } else { message = "Washing machine cycle finished."; title = "Machine END"; priority = "high"; } #ifdef ENABLE_NOTIFICATIONS if (!sendNotification(message, title, priority)) { queuedNotification = message; // Failed to send, queue it (overwrites any older one) queuedTitle = title; queuedPriority = priority; } #else Serial.println("Notification (Disabled): " + title + " - " + message); #endif } #ifdef ENABLE_DISPLAY // Update display data displayData[0] = "Vibration: " + String(isVibrationActive ? "ON" : "OFF"); displayData[1] = "Light: " + String(currentLightLevel) + " lx"; displayData[2] = "Light Active: " + String(isLightActive ? "ON" : "OFF"); displayData[3] = "Device: " + String(isDeviceActive ? "ACTIVE" : "INACTIVE"); displayDataLines = 4; // Update display updateDisplay(); #endif } bool sendNotification(String message, String title, String priority) { #ifdef ENABLE_WIFI if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String url = "http://ntfy.sh/" + String(NTFY_TOPIC); http.begin(url); http.addHeader("Content-Type", "text/plain"); http.addHeader("Title", title); http.addHeader("Priority", priority); int httpResponseCode = http.POST(message); http.end(); if (httpResponseCode > 0) { Serial.print("HTTP Response code: "); Serial.println(httpResponseCode); return true; } else { Serial.print("Error on sending POST: "); Serial.println(httpResponseCode); return false; } } else { Serial.println("WiFi Disconnected. Cannot send notification."); return false; } #else Serial.println("WiFi Disabled. Notification skipped."); return true; #endif } void updateDisplay() { #ifdef ENABLE_DISPLAY unsigned long currentTime = millis(); // Scroll logic if ((displayDataLines > displayTextLines) && (currentTime - lastDisplayScrollTime > DISPLAY_SCROLL_INTERVAL)) { lastDisplayScrollTime = currentTime; currentDisplayLine = (currentDisplayLine + 1) % displayDataLines; } display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); for (int i = 0; i < displayDataLines; i++) { int lineIndex = (currentDisplayLine + i) % displayDataLines; display.println(displayData[lineIndex]); } display.display(); #endif }