OC
OceanRemote
Low-code IoT platform
← Back to Course

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/HumidityAir temp & humidity$3-10DHT22 or BME280
📊 Barometric PressureWeather prediction$5-15BMP280 or BME280
💨 Wind Speed (Anemometer)Wind speed (km/h)$15-30Reed switch or hall effect
🌧️ Rain GaugeRainfall (mm)$10-25Tipping bucket
🧭 Wind DirectionWind direction$10-20Potentiometer or reed switches
☀️ Light SensorSolar radiation$5-10Photoresistor or BH1750
📡 ControllerData processing$5-10ESP32 (WiFi + Bluetooth)
💡 Budget Weather Station (~$50):

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 Conditions20-28°C, 40-70% RH, 1013-1020 hPa✅ Normal operations, optimal growth
🔥 Heat Wave RiskTemp > 32°C💧 Increase irrigation, apply shade cloth
❄️ Frost RiskTemp < 5°C🌿 Cover crops, irrigate before frost
🌧️ Rain ComingPressure dropping, wind increasing⏸️ Skip irrigation, prepare for rain
💧 Disease RiskHumidity > 85% for >6 hours💨 Increase ventilation, apply fungicide
💨 High WindWind > 25 km/h⏸️ Delay spraying, secure structures
💡 Solar Power for Weather Station:

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!

📖 Case Study - Weather Station Saves Coffee Farm:

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

🎉 Congratulations!

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!

📋 Quick Reference - Weather Station Troubleshooting:
┌─────────────────────────────────────────────────────────────────────────────┐
│                    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                              │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
💡 Key Takeaways:
  • 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.