#include #include #include // Wifi (Hotspot) const char wifiSSID[] = "FreeWifi"; const char wifiPASS[] = "temppassword"; // ESP8266 (Wifi module) SoftwareSerial wifiSerial(2, 3); // RX = 2, TX = 3 const bool DEBUG = true; // Serial Monitor debug output const int responseTime = 100; // LCD Display #define DHT_SENSOR_TYPE DHT_TYPE_11 static const int DHT_SENSOR_PIN = A1; DHT_nonblocking dht_sensor(DHT_SENSOR_PIN, DHT_SENSOR_TYPE); LiquidCrystal lcd(7, 8, 9, 10, 11, 12); // RS, EN, D4, D5, D6, D7 // Light sensor and LEDs int lightPin = A0; // LDR int dataPin = 4; // DS int latchPin = 5; // ST_CP int clockPin = 6; // SH_CP int leds = 0; // API const float NO_TEMP = -999.0; const unsigned long API_INTERVAL = 600000UL; // 10m const unsigned long RETRY_INTERVAL = 30000UL; // 30s unsigned long lastApiFetch = 0; unsigned long fetchInterval = API_INTERVAL; float outdoorTemp = NO_TEMP; // HTTP GET request const char HTTP_REQUEST[] PROGMEM = "GET /v1/forecast?latitude=49.46998&longitude=8.4819¤t=temperature_2m HTTP/1.1\r\n" "Host: api.open-meteo.com\r\n" "Accept-Encoding: identity\r\n" "Connection: close\r\n" "\r\n"; // Helpers // Clear serial buffer void flushInput() { while (wifiSerial.available()) wifiSerial.read(); } // Discard incoming data for a given time void drain(unsigned long ms) { unsigned long start = millis(); while (millis() - start < ms) flushInput(); } // Send AT command void espSend(const __FlashStringHelper *cmd) { flushInput(); wifiSerial.println(cmd); } // Wait for specific responses uint8_t waitForEither(const char *a, const char *b, unsigned long timeoutMs) { const uint8_t na = strlen(a); const uint8_t nb = strlen(b); char wa[24] = {0}; char wb[24] = {0}; unsigned long start = millis(); while (millis() - start < timeoutMs) { while (wifiSerial.available()) { char c = wifiSerial.read(); if (DEBUG) Serial.write(c); memmove(wa, wa + 1, na - 1); wa[na - 1] = c; memmove(wb, wb + 1, nb - 1); wb[nb - 1] = c; if (memcmp(wa, a, na) == 0) return 1; if (memcmp(wb, b, nb) == 0) return 2; } } return 0; } bool waitFor(const char *token, unsigned long timeoutMs) { return waitForEither(token, token, timeoutMs) == 1; } // Parse float from serial stream float readNumber(unsigned long timeoutMs) { char buf[10]; uint8_t i = 0; bool done = false; unsigned long start = millis(); while (!done && millis() - start < timeoutMs) { while (wifiSerial.available()) { char c = wifiSerial.read(); if (c == ',' || c == '}') { done = true; break; } if (i < sizeof(buf) - 1) buf[i++] = c; } } buf[i] = '\0'; if (!done || i == 0) return NO_TEMP; return atof(buf); } // Connect to Wifi bool joinWifi() { espSend(F("AT+CWMODE=1")); waitForEither("OK", "ERROR", 1000); flushInput(); wifiSerial.print(F("AT+CWJAP=\"")); wifiSerial.print(wifiSSID); wifiSerial.print(F("\",\"")); wifiSerial.print(wifiPASS); wifiSerial.println('"'); if (waitForEither("OK", "FAIL", 20000) != 1) return false; espSend(F("AT+CIPDNS_CUR=1,\"8.8.8.8\",\"8.8.4.4\"")); waitForEither("OK", "ERROR", 1000); espSend(F("AT+CIPMUX=0")); waitForEither("OK", "ERROR", 1000); return true; } // Check Wifi bool ensureWifi() { espSend(F("AT+CWJAP?")); if (waitForEither("+CWJAP:", "No AP", 3000) == 1) return true; Serial.println(F("\nWi-Fi not connected, (re)joining...")); return joinWifi(); } // API Temperature float fetchOutdoorTemp() { Serial.println(F("\n--- Fetching outdoor temp ---")); drain(300); if (!ensureWifi()) { Serial.println(F("\nERROR: no Wi-Fi")); return NO_TEMP; } drain(200); espSend(F("AT+CIPCLOSE")); waitForEither("OK", "ERROR", 1000); espSend(F("AT+CIPSTART=\"TCP\",\"api.open-meteo.com\",80")); if (waitForEither("OK", "ERROR", 10000) != 1) { Serial.println(F("\nERROR: could not open TCP socket")); return NO_TEMP; } flushInput(); wifiSerial.print(F("AT+CIPSEND=")); wifiSerial.println(strlen_P(HTTP_REQUEST)); if (!waitFor(">", 3000)) { Serial.println(F("\nERROR: no '>' prompt from ESP")); espSend(F("AT+CIPCLOSE")); waitForEither("OK", "ERROR", 1000); return NO_TEMP; } wifiSerial.print((const __FlashStringHelper *)HTTP_REQUEST); float t = NO_TEMP; if (waitFor("\"current\":", 8000) && waitFor("\"temperature_2m\":", 2000)) { t = readNumber(2000); } espSend(F("AT+CIPCLOSE")); waitForEither("OK", "ERROR", 1000); if (t == NO_TEMP) { Serial.println(F("\nERROR: no temperature in response")); } else { Serial.print(F("\nSUCCESS! Outdoor temp: ")); Serial.println(t); } return t; } // Write to LCD display void lcdLine(uint8_t row, const char *text) { lcd.setCursor(0, row); uint8_t i = 0; for (; text[i] != '\0' && i < 16; i++) lcd.write(text[i]); for (; i < 16; i++) lcd.write(' '); } // LEDs for light sensor void updateShiftRegister() { digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, LSBFIRST, leds); digitalWrite(latchPin, HIGH); } // Measure temperature and humidity static bool measure_environment(float *temp, float *hum) { static unsigned long timestamp = millis(); if (millis() - timestamp > 3000ul) { if (dht_sensor.measure(temp, hum) == true) { timestamp = millis(); return true; } } return false; } // Main methods void setup() { pinMode(latchPin, OUTPUT); pinMode(dataPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(13, OUTPUT); Serial.begin(115200); lcd.begin(16, 2); lcdLine(0, "Init WiFi..."); lcdLine(1, ""); wifiSerial.begin(9600); delay(1000); bool alive = false; for (uint8_t i = 0; i < 3 && !alive; i++) { espSend(F("AT")); alive = (waitForEither("OK", "ERROR", 1000) == 1); } if (!alive) { Serial.println(F("\nESP8266 does not answer.")); lcdLine(0, "ESP not found"); delay(2000); } else { espSend(F("ATE0")); waitForEither("OK", "ERROR", 1000); lcdLine(0, "Connecting WiFi"); if (joinWifi()) { lcdLine(0, "WiFi Connected!"); } else { lcdLine(0, "WiFi failed"); } delay(1000); } lcd.clear(); outdoorTemp = fetchOutdoorTemp(); lastApiFetch = millis(); fetchInterval = (outdoorTemp == NO_TEMP) ? RETRY_INTERVAL : API_INTERVAL; } void loop() { float indoorTemp; float indoorHum; if (measure_environment(&indoorTemp, &indoorHum) == true) { char tempStr[10]; dtostrf(indoorTemp, 4, 1, tempStr); char line0[20]; snprintf(line0, sizeof(line0), "In: %s C", tempStr); lcdLine(0, line0); } if (millis() - lastApiFetch >= fetchInterval) { float t = fetchOutdoorTemp(); lastApiFetch = millis(); if (t != NO_TEMP) { outdoorTemp = t; fetchInterval = API_INTERVAL; } else { fetchInterval = RETRY_INTERVAL; } } if (outdoorTemp != NO_TEMP) { char outStr[10]; dtostrf(outdoorTemp, 4, 1, outStr); char line1[20]; snprintf(line1, sizeof(line1), "Out: %s C", outStr); lcdLine(1, line1); } else { lcdLine(1, "Out: API Error"); } int reading = analogRead(lightPin); int numLEDSLit = reading / 57; if (numLEDSLit > 8) numLEDSLit = 8; leds = 0; for (int i = 0; i < numLEDSLit; i++) { leds = leds + (1 << i); } updateShiftRegister(); delay(responseTime); }