OC
OceanRemote
Low-code IoT platform
โ† Back to Course

Identifying Daily and Seasonal Patterns

Identifying Daily and Seasonal Patterns

๐Ÿ“ˆ Identifying Farm Data Patterns - From Raw Data to Actionable Insights

๐Ÿ” What You'll Learn in This Lesson:

  • ๐ŸŒก๏ธ Detect daily, weekly, and seasonal patterns in your farm data
  • ๐Ÿ“Š Use moving averages to smooth noisy sensor readings
  • ๐Ÿ”ฎ Predict future conditions based on historical patterns
  • ๐Ÿšจ Identify anomalies before they become problems
  • ๐Ÿ’ป Build a pattern detection system with Arduino/ESP32

๐Ÿ“Š Why Pattern Recognition Matters for Your Farm

Without Pattern Recognition With Pattern Recognition
โŒ React to problems after damage occurs โœ… Predict problems before they happen
โŒ Noisy sensor data causes false readings โœ… Smooth data reveals true trends
โŒ Can't distinguish real issues from normal variation โœ… Know what's normal vs. abnormal
โŒ Manual analysis takes hours โœ… Automated detection saves time

๐Ÿ“ˆ Common Farm Data Patterns

๐ŸŒก๏ธ
Daily Temperature Cycle
Pattern: Temperature lowest at sunrise (6-7 AM), peaks at 2-4 PM
Action: Irrigate early morning (5-7 AM) to reduce evaporation loss
๐Ÿ’ง
Soil Moisture Diurnal Pattern
Pattern: Moisture drops during day (evaporation + transpiration), recovers at night
Action: Normal drop = 5-15% daily. Larger drop = increase irrigation
๐Ÿ“…
Weekly Irrigation Gap
Pattern: Monday moisture lower than Friday (weekend irrigation skipped)
Action: Automate irrigation or adjust weekend schedule
๐ŸŒง๏ธ
Post-Rain Recovery
Pattern: Moisture spikes after rain, then gradual decline
Action: Soil should stay above threshold for 3-7 days after good rain
๐Ÿ“ˆ
Growth Stage Water Demand
Pattern: Low water at planting โ†’ Peak at flowering โ†’ Drop at harvest
Action: Plan irrigation schedule around crop development stages
โšก
Anomaly Detection
Pattern: Sudden drop/spike outside normal range = PROBLEM!
Action: Investigate immediately - broken sensor, leak, or pest damage

๐Ÿ“Š Moving Average - Smoothing Noisy Data

๐Ÿ’ก What is a Moving Average?

Raw sensor data often has random noise. A moving average smooths out these fluctuations so you can see the REAL trend.

Moving Average = (Valueโ‚ + Valueโ‚‚ + ... + Valueโ‚™) / N

Time Raw Sensor 3-Point Moving Average 5-Point Moving Average
8:0045--
9:0047--
10:004646.0-
11:004847.0-
12:007355.751.8
13:004254.351.2
14:004453.050.6
15:0045-49.2

Notice how the 12:00 spike (73) is smoothed out - the 5-point average shows 51.8, much closer to reality!

๐Ÿ“ˆ Trend Detection: Going Up or Down?

๐Ÿ” Linear Regression - Detecting Trends:

Simple trend detection: Compare today's average to 3-day average

  • ๐Ÿ“ˆ UP trend: Today's value > 3-day average (soil drying out)
  • ๐Ÿ“‰ DOWN trend: Today's value < 3-day average (getting wetter)
  • โžก๏ธ STABLE: Within 5% of average (normal conditions)

๐Ÿ’ป Arduino/ESP32 Code: Complete Pattern Detection System

/*
 * Farm Data Pattern Detection System
 * Detects daily cycles, weekly patterns, and anomalies
 * 
 * Features:
 * - Moving average smoothing
 * - Trend detection (going up/down)
 * - Anomaly alerts
 * - Weekly pattern detection
 */

#include 

// ========== SENSOR CONFIGURATION ==========
#define SOIL_PIN 32
#define TEMP_PIN 4

// ========== DATA STORAGE ==========
const int MAX_HOURS = 168;  // Store 7 days of data (24*7)
float soilHistory[MAX_HOURS];
float tempHistory[MAX_HOURS];
int dataIndex = 0;
int dataCount = 0;

// ========== PATTERN DETECTION ==========
struct DailyPattern {
    float morningAvg;   // 5-7 AM
    float middayAvg;    // 12-2 PM  
    float eveningAvg;   // 6-8 PM
    float nightAvg;     // 10 PM - 4 AM
};

// ========== READ SOIL MOISTURE ==========
int readSoilMoisture() {
    int raw = analogRead(SOIL_PIN);
    // Calibrate these values for YOUR sensor!
    int percentage = map(raw, 3800, 1500, 0, 100);
    return constrain(percentage, 0, 100);
}

// ========== MOVING AVERAGE ==========
float calculateMovingAverage(float* data, int windowSize) {
    if (dataCount < windowSize) return -1;
    
    float sum = 0;
    for (int i = dataCount - windowSize; i < dataCount; i++) {
        sum += data[i];
    }
    return sum / windowSize;
}

// ========== DETECT PATTERNS ==========
void detectDailyPattern() {
    if (dataCount < 24) return;
    
    DailyPattern pattern;
    pattern.morningAvg = 0;
    pattern.middayAvg = 0;
    pattern.eveningAvg = 0;
    pattern.nightAvg = 0;
    
    int morningCount = 0, middayCount = 0, eveningCount = 0, nightCount = 0;
    int currentHour = (millis() / 3600000) % 24;
    
    // Analyze last 24 hours
    for (int i = 0; i < 24; i++) {
        int hour = (currentHour - i + 24) % 24;
        float moisture = soilHistory[(dataIndex - 1 - i + MAX_HOURS) % MAX_HOURS];
        
        if (hour >= 5 && hour <= 7) {
            pattern.morningAvg += moisture;
            morningCount++;
        } else if (hour >= 12 && hour <= 14) {
            pattern.middayAvg += moisture;
            middayCount++;
        } else if (hour >= 18 && hour <= 20) {
            pattern.eveningAvg += moisture;
            eveningCount++;
        } else if (hour >= 22 || hour <= 4) {
            pattern.nightAvg += moisture;
            nightCount++;
        }
    }
    
    if (morningCount > 0) pattern.morningAvg /= morningCount;
    if (middayCount > 0) pattern.middayAvg /= middayCount;
    if (eveningCount > 0) pattern.eveningAvg /= eveningCount;
    if (nightCount > 0) pattern.nightAvg /= nightCount;
    
    Serial.println("๐Ÿ“Š DAILY PATTERN DETECTED:");
    Serial.printf("   ๐ŸŒ… Morning (5-7 AM):   %.1f%%\n", pattern.morningAvg);
    Serial.printf("   โ˜€๏ธ Midday (12-2 PM):   %.1f%%\n", pattern.middayAvg);
    Serial.printf("   ๐ŸŒ™ Evening (6-8 PM):   %.1f%%\n", pattern.eveningAvg);
    Serial.printf("   ๐ŸŒƒ Night (10 PM-4 AM): %.1f%%\n", pattern.nightAvg);
    
    // Calculate daily drop
    float dailyDrop = pattern.morningAvg - pattern.eveningAvg;
    if (dailyDrop > 15) {
        Serial.println("   โš ๏ธ Large daily moisture drop (>15%) - Increase irrigation!");
    } else if (dailyDrop < 2) {
        Serial.println("   โœ… Very small daily drop - Normal evaporation");
    }
}

// ========== DETECT WEEKLY PATTERN ==========
void detectWeeklyPattern() {
    if (dataCount < 168) return; // Need 7 days of data
    
    float dayAvg[7] = {0};
    int dayCount[7] = {0};
    
    // Analyze last 7 days
    for (int i = 0; i < 168; i++) {
        int day = (i / 24) % 7;
        float moisture = soilHistory[(dataIndex - 1 - i + MAX_HOURS) % MAX_HOURS];
        dayAvg[day] += moisture;
        dayCount[day]++;
    }
    
    for (int d = 0; d < 7; d++) {
        if (dayCount[d] > 0) dayAvg[d] /= dayCount[d];
    }
    
    Serial.println("๐Ÿ“… WEEKLY PATTERN DETECTED:");
    const char* days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    for (int d = 0; d < 7; d++) {
        Serial.printf("   %s: %.1f%%\n", days[d], dayAvg[d]);
    }
    
    // Check for weekend irrigation gap
    float weekendAvg = (dayAvg[0] + dayAvg[6]) / 2;  // Sun + Sat
    float weekdayAvg = (dayAvg[1] + dayAvg[2] + dayAvg[3] + dayAvg[4] + dayAvg[5]) / 5;
    
    if (weekendAvg < weekdayAvg - 10) {
        Serial.println("   โš ๏ธ Weekend irrigation gap detected! Monday moisture significantly lower.");
        Serial.println("   ๐Ÿ’ก Action: Automate weekend irrigation or adjust schedule.");
    }
}

// ========== ANOMALY DETECTION ==========
void detectAnomalies(float currentMoisture) {
    if (dataCount < 24) return;
    
    float avg24h = calculateMovingAverage(soilHistory, 24);
    if (avg24h <= 0) return;
    
    float deviation = abs(currentMoisture - avg24h);
    float percentDeviation = (deviation / avg24h) * 100;
    
    if (percentDeviation > 30) {
        Serial.println("๐Ÿšจ ANOMALY DETECTED! Current reading deviates >30% from 24-hour average!");
        Serial.printf("   Current: %.1f%% | 24h Avg: %.1f%% | Deviation: %.1f%%\n", 
                     currentMoisture, avg24h, percentDeviation);
        Serial.println("   ๐Ÿ” Possible causes: Sensor failure, water leak, flooding, or sensor moved.");
    }
}

// ========== TREND DETECTION ==========
void detectTrend() {
    if (dataCount < 6) return;
    
    float shortAvg = calculateMovingAverage(soilHistory, 3);  // Last 3 readings
    float longAvg = calculateMovingAverage(soilHistory, 12);  // Last 12 readings (3 hours)
    
    if (shortAvg > longAvg * 1.05) {
        Serial.println("๐Ÿ“ˆ TREND: Soil moisture INCREASING (getting wetter)");
        if (shortAvg > 70) {
            Serial.println("   โš ๏ธ Approaching over-watering - Consider reducing irrigation.");
        }
    } else if (shortAvg < longAvg * 0.95) {
        Serial.println("๐Ÿ“‰ TREND: Soil moisture DECREASING (drying out)");
        if (shortAvg < 35) {
            Serial.println("   โš ๏ธ Nearing dry threshold - Plan irrigation.");
        }
    } else {
        Serial.println("โžก๏ธ TREND: Soil moisture STABLE");
    }
}

// ========== PREDICT NEXT VALUE ==========
float predictNextValue() {
    if (dataCount < 6) return -1;
    
    // Simple linear extrapolation using last 6 readings
    float sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
    int n = min(6, dataCount);
    
    for (int i = 0; i < n; i++) {
        float x = i + 1;
        float y = soilHistory[(dataIndex - 1 - i + MAX_HOURS) % MAX_HOURS];
        sumX += x;
        sumY += y;
        sumXY += x * y;
        sumX2 += x * x;
    }
    
    float slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
    float intercept = (sumY - slope * sumX) / n;
    
    // Predict next value (x = 0)
    float nextValue = intercept;
    return nextValue;
}

// ========== MAIN FUNCTIONS ==========
void setup() {
    Serial.begin(115200);
    pinMode(SOIL_PIN, INPUT);
    
    Serial.println("========================================");
    Serial.println("๐Ÿ“Š FARM DATA PATTERN DETECTION SYSTEM");
    Serial.println("   Identify trends, cycles, and anomalies");
    Serial.println("========================================\n");
}

void loop() {
    int moisture = readSoilMoisture();
    int currentHour = (millis() / 3600000) % 24;
    
    // Store data
    soilHistory[dataIndex] = moisture;
    dataIndex = (dataIndex + 1) % MAX_HOURS;
    if (dataCount < MAX_HOURS) dataCount++;
    
    // Display current reading
    Serial.println("โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”");
    Serial.printf("๐Ÿ• Time: %02d:00\n", currentHour);
    Serial.printf("๐Ÿ’ง Current soil moisture: %d%%\n", moisture);
    
    // Run pattern detection
    detectAnomalies(moisture);
    detectTrend();
    
    // Run daily analysis at 7 AM
    if (currentHour == 7) {
        detectDailyPattern();
        detectWeeklyPattern();
        
        float nextValue = predictNextValue();
        if (nextValue > 0) {
            Serial.printf("๐Ÿ”ฎ Predicted moisture next hour: %.1f%%\n", nextValue);
        }
    }
    
    Serial.println("โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n");
    
    delay(3600000); // Read every hour
}
    

๐Ÿ“ฑ Real-Time Dashboard Visualization

/*
 * Send pattern data to OceanRemote dashboard
 * Visualize trends and get alerts
 */

#include 
#include 

const char* ssid = "YOUR_WIFI";
const char* password = "YOUR_PASSWORD";
const char* token = "YOUR_DEVICE_TOKEN";

void sendPatternData(float moisture, float trend, float prediction) {
    if (WiFi.status() != WL_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(token);
    data += "&soil_moisture=" + String(moisture);
    data += "&trend=" + String(trend);
    data += "&prediction=" + String(prediction);
    data += "&pattern_detected=true";
    
    http.POST(data);
    http.end();
}
    
๐Ÿ“Š Case Study - Pattern Detection Saves Tomato Crop:

A Moroccan tomato farmer used pattern detection to identify a hidden problem:

  • ๐Ÿ“ˆ Pattern: Soil moisture was dropping 25% daily (normal is 8-12%)
  • ๐Ÿ” Investigation: Found underground pipe leak losing 5,000L/hour
  • ๐Ÿ’ฐ Savings: Fixed leak โ†’ saved 120,000L/day, $500/month
  • ๐ŸŒฑ Result: Tomatoes recovered, yield increased 35%

"Without pattern detection, we would have kept watering more and never found the leak." - Farm owner, Morocco

๐Ÿ’ก Pro Tips for Pattern Detection:
  • ๐Ÿ“ Start simple: Track daily min/max and look for abnormalities
  • ๐Ÿ“ Use baseline data: Collect 1-2 weeks of data before setting thresholds
  • ๐Ÿ“ Multiple sensors: Patterns across zones reveal systemic issues
  • ๐Ÿ“ Visualize: Plot data to see patterns your eyes can detect instantly
  • ๐Ÿ“ Automate alerts: Get notifications when patterns break
๐ŸŽ‰ Congratulations!

You can now identify and act on farm data patterns!

  • โœ… Detect daily temperature and moisture cycles
  • โœ… Use moving averages to smooth noisy data
  • โœ… Identify weekly patterns (weekend irrigation gaps)
  • โœ… Detect anomalies before they become disasters
  • โœ… Predict future conditions from historical patterns
๐Ÿ“‹ Quick Reference - Pattern Recognition Cheat Sheet:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      WHAT TO LOOK FOR IN YOUR FARM DATA                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                             โ”‚
โ”‚  ๐ŸŒก๏ธ TEMPERATURE PATTERNS:                                                  โ”‚
โ”‚     โ€ข Min temp: Sunrise (6-7 AM)                                           โ”‚
โ”‚     โ€ข Max temp: 2-4 PM                                                      โ”‚
โ”‚     โ€ข Normal daily range: 8-15ยฐC difference                                โ”‚
โ”‚     โ€ข ALERT: Night temp > 25ยฐC = pollination problems                     โ”‚
โ”‚                                                                             โ”‚
โ”‚  ๐Ÿ’ง SOIL MOISTURE PATTERNS:                                                 โ”‚
โ”‚     โ€ข Normal daily drop: 5-15%                                             โ”‚
โ”‚     โ€ข Post-rain recovery: Should stay above threshold 3-7 days             โ”‚
โ”‚     โ€ข ALERT: Drop > 20% daily = leak or extreme heat                       โ”‚
โ”‚                                                                             โ”‚
โ”‚  ๐Ÿ“… WEEKLY PATTERNS:                                                        โ”‚
โ”‚     โ€ข Monday moisture lower = weekend irrigation gap                       โ”‚
โ”‚     โ€ข ALERT: Day-to-day variation > 30% = inconsistent watering            โ”‚
โ”‚                                                                             โ”‚
โ”‚  ๐Ÿšจ ANOMALIES (Investigate Immediately):                                    โ”‚
โ”‚     โ€ข Sudden 50%+ moisture drop within 2 hours                             โ”‚
โ”‚     โ€ข Temperature spike 10ยฐC above normal                                  โ”‚
โ”‚     โ€ข Readings stuck at same value for hours                               โ”‚
โ”‚     โ€ข Night values same as day values                                      โ”‚
โ”‚                                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
๐Ÿ’ก 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.