#include #include #include #include // ========================================== // 1. CONFIGURATION // ========================================== const char* ssid = "css_STEAM"; const char* password = ""; // Update this! // Pins #define I2C_SDA 7 #define I2C_SCL 8 #define SERVO_PIN 22 // Servo Settings (ESP32 v3.0+) #define PWM_FREQ 50 #define PWM_RES 16 #define MIN_DUTY 1638 // 0 degrees #define MAX_DUTY 8191 // 180 degrees // Weather API (Hong Kong Coordinates) const String weatherURL = "https://api.open-meteo.com/v1/forecast?latitude=22.31&longitude=114.16¤t_weather=true"; // Objects LiquidCrystal_I2C lcd(0x27, 16, 2); WebServer server(80); // Global Variables float currentTemp = 0.0; String ipAddress = "0.0.0.0"; // ========================================== // 2. HELPER FUNCTIONS // ========================================== // Move Servo to specific angle void setServo(int angle) { angle = constrain(angle, 0, 180); uint32_t duty = map(angle, 0, 180, MIN_DUTY, MAX_DUTY); ledcWrite(SERVO_PIN, duty); } // Fetch Weather from Open-Meteo void updateWeather() { HTTPClient http; http.begin(weatherURL); int httpCode = http.GET(); if (httpCode == 200) { String payload = http.getString(); // Manual String Parsing (Simpler than installing ArduinoJson) // We look for "temperature": int tempIndex = payload.indexOf("\"temperature\":"); if (tempIndex > 0) { int startIndex = tempIndex + 14; int endIndex = payload.indexOf(",", startIndex); String tempString = payload.substring(startIndex, endIndex); currentTemp = tempString.toFloat(); Serial.println("Weather Updated: " + String(currentTemp) + "C"); } } else { Serial.println("Weather Error: " + String(httpCode)); } http.end(); } // Scroll Text on LCD Line 1 (Line 0 is top, Line 1 is bottom) void scrollText(String message, int row, int delayTime) { for (int i = 0; i < message.length() - 15; i++) { lcd.setCursor(0, row); lcd.print(message.substring(i, i + 16)); delay(delayTime); } } // ========================================== // 3. WEB HANDLERS // ========================================== // ROOT PAGE: The HTML Dashboard void handleRoot() { String html = ""; html += ""; html += "

ESP32 Control

"; html += "

Current HK Temp: " + String(currentTemp) + "°C

"; html += ""; html += ""; html += "
"; html += ""; html += ""; server.send(200, "text/html", html); } // API: Set Angle (Usage: /set?angle=45) void handleSetAngle() { if (server.hasArg("angle")) { int angle = server.arg("angle").toInt(); setServo(angle); server.send(200, "text/plain", "OK: Angle set to " + String(angle)); Serial.println("API Request: Set Angle " + String(angle)); } else { server.send(400, "text/plain", "Error: Missing 'angle' parameter"); } } // API: Swing (Usage: /swing) void handleSwing() { server.send(200, "text/plain", "OK: Swinging..."); Serial.println("API Request: Swinging"); // Simple swing animation for(int i=0; i<=180; i+=5) { setServo(i); delay(15); } for(int i=180; i>=0; i-=5) { setServo(i); delay(15); } setServo(90); // Return to center } // ========================================== // 4. MAIN SETUP // ========================================== void setup() { Serial.begin(115200); // 1. Setup LCD Wire.begin(I2C_SDA, I2C_SCL); lcd.init(); lcd.backlight(); lcd.setCursor(0, 0); lcd.print("System Booting.."); // 2. Setup Servo if (!ledcAttach(SERVO_PIN, PWM_FREQ, PWM_RES)) { Serial.println("Servo Attach Failed"); } setServo(0); // Start at 0 // 3. Connect to WiFi WiFi.begin(ssid, password); lcd.setCursor(0, 1); lcd.print("Connecting WiFi "); int dots = 0; while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); lcd.print("."); dots++; if (dots > 5) { dots = 0; lcd.setCursor(15, 1); lcd.print(" "); // Clear dots lcd.setCursor(15, 1); } // If it takes too long (> 20 seconds), scroll error if (millis() > 20000 && WiFi.status() != WL_CONNECTED) { lcd.clear(); lcd.print("WiFi Error!"); scrollText("Check SSID/Password... Retrying... ", 1, 300); } } // 4. Connection Success ipAddress = WiFi.localIP().toString(); Serial.println("\nWiFi Connected! IP: " + ipAddress); // 5. Initial Weather Fetch updateWeather(); // 6. Update LCD with Info lcd.clear(); lcd.setCursor(0, 0); lcd.print("IP:"); lcd.print(ipAddress); lcd.setCursor(0, 1); lcd.print("HK:"); lcd.print(currentTemp, 1); // 1 decimal place lcd.print("C Running"); // 7. Start Web Server server.on("/", handleRoot); server.on("/set", handleSetAngle); server.on("/swing", handleSwing); server.begin(); } void loop() { server.handleClient(); // Optional: Refresh weather every 10 minutes static unsigned long lastWeatherTime = 0; if (millis() - lastWeatherTime > 600000) { updateWeather(); lcd.setCursor(0, 1); lcd.print("HK:"); lcd.print(currentTemp, 1); lcd.print("C "); lastWeatherTime = millis(); } }