Complete Weather Station Code
π€οΈ Complete Weather Station Code - Build Your Own Farm Weather Station
π¦οΈ What You'll Learn in This Lesson:
- π‘οΈ Measure temperature, humidity, barometric pressure, wind speed, and rainfall
- π Send real-time weather data to OceanRemote cloud dashboard
- π Build a low-power weather station for your farm
- π§ Integrate weather data with irrigation decisions
- πΎ Predict frost, heat waves, and optimal planting conditions
π Complete Weather Station Components
| Component | Function | Cost | Best Type |
|---|---|---|---|
| π‘οΈ Temperature/Humidity | Air temp & humidity | $3-10 | DHT22 or BME280 |
| π Barometric Pressure | Weather prediction | $5-15 | BMP280 or BME280 |
| π¨ Wind Speed (Anemometer) | Wind speed (km/h) | $15-30 | Reed switch or hall effect |
| π§οΈ Rain Gauge | Rainfall (mm) | $10-25 | Tipping bucket |
| π§ Wind Direction | Wind direction | $10-20 | Potentiometer or reed switches |
| βοΈ Light Sensor | Solar radiation | $5-10 | Photoresistor or BH1750 |
| π‘ Controller | Data processing | $5-10 | ESP32 (WiFi + Bluetooth) |
You can build a complete weather station for under $50: ESP32 ($8) + DHT22 ($5) + BME280 ($10) + DIY wind/rain sensors ($20) + solar panel ($7). This exactly matches our lesson's code!
π οΈ Complete Wiring Diagram
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
COMPLETE WEATHER STATION WIRING
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ESP32 PIN MAPPING:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β GPIO21 ββββββΊ I2C SDA (BME280/BMP280) β
β GPIO22 ββββββΊ I2C SCL (BME280/BMP280) β
β GPIO16 ββββββΊ DHT22 Data pin β
β GPIO34 ββββββΊ Rain Gauge (tipping bucket) - INPUT_PULLUP β
β GPIO35 ββββββΊ Anemometer (wind speed) - INPUT_PULLUP β
β GPIO32 ββββββΊ Wind Direction (analog) β
β GPIO33 ββββββΊ Light Sensor (photoresistor) β
β β
β BME280 (I2C): β
β VCC ββββΊ 3.3V β
β GND ββββΊ GND β
β SDA ββββΊ GPIO21 β
β SCL ββββΊ GPIO22 β
β β
β DHT22: β
β VCC ββββΊ 3.3V β
β GND ββββΊ GND β
β DATA ββββΊ GPIO16 (with 10kΞ© pull-up to 3.3V) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
RAIN GAUGE (Tipping Bucket):
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β One wire ββββΊ GPIO34 (internal pull-up enabled) β
β Other wire ββββΊ GND β
β Each tip = 0.2mm of rain β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ANEMOMETER (Wind Speed):
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β One wire ββββΊ GPIO35 (internal pull-up enabled) β
β Other wire ββββΊ GND β
β Each pulse = 0.34 km/h of wind β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π» Complete Working Weather Station Code
/* * Complete Farm Weather Station * Measures: Temperature, Humidity, Pressure, Wind Speed, Rainfall * Sends data to OceanRemote every 5 minutes * * Components: * - ESP32 Development Board * - DHT22 (Temperature + Humidity) * - BME280 (Temperature, Humidity, Pressure) * - Tipping bucket rain gauge * - Reed switch anemometer (wind speed) * * Author: OceanRemote Education * Version: 2.0 */ #include#include #include #include #include #include // ========== WIFI CONFIGURATION ========== const char* ssid = "YOUR_WIFI"; const char* password = "YOUR_PASSWORD"; const char* oceanRemoteToken = "YOUR_OCEANREMOTE_TOKEN"; // ========== PIN DEFINITIONS ========== #define DHTPIN 16 #define DHTTYPE DHT22 #define RAIN_PIN 34 // Tipping bucket rain gauge #define WIND_PIN 35 // Anemometer (wind speed) #define WIND_DIR_PIN 32 // Wind direction (analog) #define LIGHT_PIN 33 // Light sensor (optional) // ========== SENSOR CALIBRATION ========== // Rain gauge: each tip = 0.2mm of rain const float RAIN_PER_TIP = 0.2; // Anemometer: each pulse = 0.34 km/h (adjust for your sensor) const float WIND_FACTOR = 0.34; // Wind direction mapping (10-bit ADC 0-4095) struct WindDir { int minADC; int maxADC; const char* direction; float degrees; }; WindDir windDirs[] = { {0, 400, "N", 0}, {401, 800, "NE", 45}, {801, 1200, "E", 90}, {1201, 1600, "SE", 135}, {1601, 2000, "S", 180}, {2001, 2400, "SW", 225}, {2401, 2800, "W", 270}, {2801, 3200, "NW", 315}, {3201, 4095, "N", 0} }; // ========== GLOBAL VARIABLES ========== Adafruit_BME280 bme; DHT dht(DHTPIN, DHTTYPE); volatile int rainTips = 0; volatile unsigned long lastRainInterrupt = 0; volatile int windPulses = 0; volatile unsigned long lastWindInterrupt = 0; float windSpeed = 0; float totalRainToday = 0; float dailyRain = 0; float monthlyRain = 0; float yearlyRain = 0; unsigned long lastWeatherSend = 0; unsigned long lastWindCalc = 0; unsigned long lastMidnight = 0; // ========== INTERRUPT SERVICE ROUTINES ========== // Rain gauge ISR - triggered on falling edge (each bucket tip) void IRAM_ATTR rainISR() { unsigned long now = millis(); // Debounce: ignore interrupts within 50ms (prevents false triggers) if (now - lastRainInterrupt > 50) { rainTips++; lastRainInterrupt = now; } } // Anemometer ISR - triggered on falling edge (each rotation) void IRAM_ATTR windISR() { unsigned long now = millis(); // Debounce if (now - lastWindInterrupt > 10) { windPulses++; lastWindInterrupt = now; } } // ========== READ SENSORS ========== float readTemperature() { // Try DHT22 first, fallback to BME280 float temp = dht.readTemperature(); if (isnan(temp)) { temp = bme.readTemperature(); } return temp; } float readHumidity() { float hum = dht.readHumidity(); if (isnan(hum)) { hum = bme.readHumidity(); } return hum; } float readPressure() { return bme.readPressure() / 100.0F; // Convert Pa to hPa } float readHeatIndex(float temp, float humidity) { // Simplified heat index calculation float hi = temp; if (temp >= 27 && humidity >= 40) { hi = -8.78469475556 + 1.61139411 * temp + 2.33854883889 * humidity + -0.14611605 * temp * humidity + -0.012308094 * temp * temp + -0.0164248277778 * humidity * humidity + 0.002211732 * temp * temp * humidity + 0.00072546 * temp * humidity * humidity + -0.000003582 * temp * temp * humidity * humidity; } return hi; } float calculateDewPoint(float temp, float humidity) { // Magnus formula for dew point float a = 17.27; float b = 237.7; float alpha = log(humidity / 100.0) + (a * temp) / (b + temp); float dewPoint = (b * alpha) / (a - alpha); return dewPoint; } // ========== WIND DIRECTION ========== const char* getWindDirection() { int adc = analogRead(WIND_DIR_PIN); for (int i = 0; i < 9; i++) { if (adc >= windDirs[i].minADC && adc <= windDirs[i].maxADC) { return windDirs[i].direction; } } return "N"; } float getWindDirectionDegrees() { int adc = analogRead(WIND_DIR_PIN); for (int i = 0; i < 9; i++) { if (adc >= windDirs[i].minADC && adc <= windDirs[i].maxADC) { return windDirs[i].degrees; } } return 0; } // ========== LIGHT SENSOR ========== int readLightLevel() { int light = analogRead(LIGHT_PIN); // Convert to approximate lux (0-1000+) return map(light, 0, 4095, 0, 1000); } // ========== RAINFALL TOTALS ========== void updateRainTotals() { if (rainTips > 0) { float rain = rainTips * RAIN_PER_TIP; totalRainToday += rain; dailyRain += rain; monthlyRain += rain; yearlyRain += rain; rainTips = 0; Serial.printf(" π§οΈ Rain detected: +%.1f mm\n", rain); } } // ========== CHECK FOR DAILY RESET ========== void checkDailyReset() { struct tm timeinfo; if (!getLocalTime(&timeinfo, 5000)) { Serial.println("Failed to obtain time"); return; } if (timeinfo.tm_hour == 0 && timeinfo.tm_min == 0 && lastMidnight == 0) { // Reset daily rain dailyRain = 0; lastMidnight = 1; Serial.println("π Daily rain reset at midnight"); } else if (timeinfo.tm_hour != 0) { lastMidnight = 0; } } // ========== SEND DATA TO OCEANREMOTE ========== void sendToOceanRemote(float temp, float humidity, float pressure, float wind, float rain, const char* windDir, float dewPoint, float heatIndex) { if (WiFi.status() != WL_CONNECTED) { Serial.println("β WiFi not connected"); return; } HTTPClient http; http.begin("https://api.oceanremote.net/device/state"); http.addHeader("Content-Type", "application/x-www-form-urlencoded"); String data = "token=" + String(oceanRemoteToken); data += "&temperature=" + String(temp); data += "&humidity=" + String(humidity); data += "&pressure=" + String(pressure); data += "&wind_speed=" + String(wind); data += "&wind_direction=" + String(windDir); data += "&rain_today=" + String(totalRainToday); data += "&rain_rate=" + String(rainTips * 6); // mm per hour estimate data += "&dew_point=" + String(dewPoint); data += "&heat_index=" + String(heatIndex); data += "&light=" + String(readLightLevel()); int httpCode = http.POST(data); if (httpCode == 200) { Serial.println("β Weather data sent to OceanRemote"); } else { Serial.printf("β Upload failed: HTTP %d\n", httpCode); } http.end(); } // ========== DISPLAY WEATHER REPORT ========== void displayWeatherReport(float temp, float humidity, float pressure, float wind, const char* windDir, float rain, float dewPoint, float heatIndex) { Serial.println("\nββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"); Serial.println("β π€οΈ WEATHER STATION REPORT β"); Serial.println("β " + String(__DATE__) + " " + String(__TIME__) + " β"); Serial.println("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"); Serial.println("\nπ‘οΈ TEMPERATURE & HUMIDITY:"); Serial.printf(" Temperature: %.1fΒ°C | %.1fΒ°F\n", temp, temp * 9.0/5.0 + 32.0); Serial.printf(" Humidity: %.1f%%\n", humidity); Serial.printf(" Dew Point: %.1fΒ°C\n", dewPoint); Serial.printf(" Heat Index: %.1fΒ°C\n", heatIndex); Serial.printf(" Barometric Pressure: %.1f hPa\n", pressure); Serial.println("\nπ¨ WIND CONDITIONS:"); Serial.printf(" Wind Speed: %.1f km/h\n", wind); Serial.printf(" Wind Direction: %s (%.0fΒ°)\n", windDir, getWindDirectionDegrees()); Serial.println("\nπ§οΈ RAINFALL:"); Serial.printf(" Today: %.1f mm\n", totalRainToday); Serial.printf(" This month: %.1f mm\n", monthlyRain); Serial.printf(" This year: %.1f mm\n", yearlyRain); Serial.println("\nβοΈ LIGHT:"); Serial.printf(" Light Level: %d lux\n", readLightLevel()); // Weather recommendations Serial.println("\nπ FARM RECOMMENDATIONS:"); if (wind > 25) { Serial.println(" π¨ High wind - Delay spraying and irrigation"); } if (rain > 10) { Serial.println(" π§οΈ Significant rain - Skip irrigation today"); } if (temp < 5) { Serial.println(" βοΈ Frost risk - Cover sensitive crops!"); } if (temp > 35) { Serial.println(" π₯ Extreme heat - Increase irrigation, provide shade"); } if (humidity > 85) { Serial.println(" π§ High humidity - Mold risk, increase ventilation"); } if (pressure < 1010 && pressure > 1000) { Serial.println(" π Falling pressure - Rain likely within 24-48 hours"); } Serial.println("\nββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n"); } // ========== CONNECT TO WIFI ========== void connectWiFi() { Serial.print("π‘ Connecting to WiFi"); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nβ WiFi connected!"); Serial.printf("π‘ IP Address: %s\n\n", WiFi.localIP().toString().c_str()); } // ========== SETUP ========== void setup() { Serial.begin(115200); delay(1000); Serial.println("βββββββββββββββββββββββββββββββββββββββββββ"); Serial.println("π€οΈ PROFESSIONAL FARM WEATHER STATION v2.0"); Serial.println(" OceanRemote IoT Weather Monitor"); Serial.println("βββββββββββββββββββββββββββββββββββββββββββ\n"); // Initialize I2C Wire.begin(); // Initialize BME280 if (!bme.begin(0x76)) { Serial.println("β οΈ BME280 not found at 0x76, trying 0x77..."); if (!bme.begin(0x77)) { Serial.println("β BME280 sensor not found!"); } else { Serial.println("β BME280 initialized at 0x77"); } } else { Serial.println("β BME280 initialized at 0x76"); } // Initialize DHT22 dht.begin(); Serial.println("β DHT22 initialized"); // Configure interrupt pins pinMode(RAIN_PIN, INPUT_PULLUP); pinMode(WIND_PIN, INPUT_PULLUP); pinMode(WIND_DIR_PIN, INPUT); pinMode(LIGHT_PIN, INPUT); // Attach interrupts attachInterrupt(digitalPinToInterrupt(RAIN_PIN), rainISR, FALLING); attachInterrupt(digitalPinToInterrupt(WIND_PIN), windISR, FALLING); Serial.println("β Interrupts configured for rain and wind sensors"); // Connect to WiFi connectWiFi(); // Get initial time for daily reset configTime(0, 0, "pool.ntp.org", "time.nist.gov"); Serial.println("β° Time synchronized via NTP"); Serial.println("\nβ Weather station ready!"); Serial.println(" Data will be sent every 5 minutes\n"); } // ========== MAIN LOOP ========== void loop() { unsigned long now = millis(); // Calculate wind speed every 1 second if (now - lastWindCalc >= 1000) { windSpeed = windPulses * WIND_FACTOR; windPulses = 0; lastWindCalc = now; } // Update rain totals updateRainTotals(); // Check for daily reset checkDailyReset(); // Send data every 5 minutes (300,000 ms) if (now - lastWeatherSend >= 300000) { // Read all sensors float temp = readTemperature(); float humidity = readHumidity(); float pressure = readPressure(); float dewPoint = calculateDewPoint(temp, humidity); float heatIndex = readHeatIndex(temp, humidity); const char* windDir = getWindDirection(); // Validate readings if (isnan(temp) || isnan(humidity)) { Serial.println("β οΈ Sensor error - using last valid values"); } else { // Display and send data displayWeatherReport(temp, humidity, pressure, windSpeed, windDir, totalRainToday, dewPoint, heatIndex); sendToOceanRemote(temp, humidity, pressure, windSpeed, totalRainToday, windDir, dewPoint, heatIndex); } lastWeatherSend = now; } // Update wind speed display every 5 seconds (optional) static unsigned long lastWindDisplay = 0; if (now - lastWindDisplay >= 5000) { Serial.printf("π¨ Current wind speed: %.1f km/h\n", windSpeed); lastWindDisplay = now; } delay(100); }
π Data Interpretation Guide
| Weather Condition | Sensor Reading | Farm Action |
|---|---|---|
| βοΈ Ideal Growing Conditions | 20-28Β°C, 40-70% RH, 1013-1020 hPa | β Normal operations, optimal growth |
| π₯ Heat Wave Risk | Temp > 32Β°C | π§ Increase irrigation, apply shade cloth |
| βοΈ Frost Risk | Temp < 5Β°C | πΏ Cover crops, irrigate before frost |
| π§οΈ Rain Coming | Pressure dropping, wind increasing | βΈοΈ Skip irrigation, prepare for rain |
| π§ Disease Risk | Humidity > 85% for >6 hours | π¨ Increase ventilation, apply fungicide |
| π¨ High Wind | Wind > 25 km/h | βΈοΈ Delay spraying, secure structures |
You can run this weather station off-grid using: 6V/2W solar panel ($7) + TP4056 charging module ($2) + 18650 battery ($5) + MT3608 boost converter ($3) to get 5V/3.3V. The ESP32 in deep sleep between readings consumes only ~0.3mA!
A coffee farm in Ethiopia installed a weather station to monitor conditions:
- π Discovery: Humidity >85% for 8+ hours every night during rainy season
- π¦ Impact: Coffee berry disease was spreading due to prolonged leaf wetness
- β Action: Installed fans to circulate air when humidity >80%
- π Result: 60% reduction in disease, 25% increase in quality grade
"The weather station paid for itself in one season just by preventing disease!" - Coffee Farmer, Ethiopia
You now have a complete professional weather station for your farm!
- β Temperature, humidity, pressure, wind, rain, light
- β Automatic data logging to OceanRemote cloud
- β Real-time farm recommendations based on weather
- β Solar power ready for remote locations
- β Integration with irrigation decisions
Next step: Connect your weather station data to your irrigation controller for fully automated, weather-aware smart farming!
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β WEATHER STATION TROUBLESHOOTING GUIDE β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β β β β DHT22 reads NaN: β β β’ Check pull-up resistor (10kΞ© between DATA and 3.3V) β β β’ Add 2-second delay between readings β β β’ Replace sensor if still failing β β β β β BME280 not detected: β β β’ Check I2C address (0x76 vs 0x77) β β β’ Verify SDA/SCL connections β β β’ Try I2C scanner sketch to find address β β β β β No rain readings: β β β’ Check interrupt pin (use GPIOs 34,35,36,39 only for external) β β β’ Enable INPUT_PULLUP in pinMode β β β’ Verify tipping bucket wiring β β β β β Interrupts not firing: β β β’ GPIOs 34-39 are input-only (cannot use internal pull-up!) β β β’ Add external 10kΞ© pull-up resistors β β β’ Use gpio_set_intr_type() for these pins β β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Apply these concepts directly to your farm or project.
- Take notes on important details for the quiz.
- Use the button below to track your progress.