This is an old revision of the document!


Remote Weather Station - NIŢU Gabriel

Introducere

Proiectul consta intr-o statie meteo remote care aduna informatii despre temperatura, umiditate, presiunea atmosferica, nivelul de intensitate luminoasa si predictie a ploii si transmite toate aceste date prin wifi catre un webserver. Webserverul le afiseaza intr-un mod interactiv si usor de vizualizat si analizat pe un dashboard.

Varianta finala si functionala a proiectului se poate vedea pe urmatorul link https://youtu.be/XK-8px-DiMA?feature=shared

Descriere generală

Schema bloc

Hardware Design

Software Design

Mediul de dezvoltare:

  • Arduino IDE
  • VS Code

Pe Arduino am implementat citirea senzorului DHT si trimiterea informatiilor catre ESP32.

SoftwareSerial espSerial(rxPin, txPin);

void setup() {
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
    
  Serial.begin(9600);
  espSerial.begin(9600);
  dht.begin();
}

void loop() {
  Serial.println("Sending to ESP32...");
  int16_t  value = 1234;


  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();

  if (isnan(humidity) || isnan(temperature)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  espSerial.println(temperature);
  espSerial.println(humidity);

  Serial.print("Humidity: ");
  Serial.print(humidity);
  Serial.print(" %\t");
  
  Serial.print("Temperature: ");
  Serial.print(temperature);
  Serial.println(" *C");

  delay(2000);
}

Pe ESP32 citesc informatiile de la senzorul bmp, primesc datele de la Arduino si le trimit pe toate prin Wifi catre un server implementat de mine.

void setup() {
  Serial.begin(9600);  // For Serial Monitor
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // UART2
  int counter = 0;

  delay(2000);  // Wait for power to stabilize
  WiFi.disconnect(true);  // Clear previous settings
  delay(1000);

  Serial.println("Scanning Wi-Fi...");
  int networks = WiFi.scanNetworks();

  if (networks == 0) {
    Serial.println("No networks found.");
  } else {
    Serial.println("Networks found:");
    for (int i = 0; i < networks; ++i) {
      Serial.println(WiFi.SSID(i));
    }
  }
  
  WiFi.begin(ssid, password);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" Connected!");
  

   Wire.begin(21, 22); // SDA = 21, SCL = 22
   if (!bmp.begin()) {
    Serial.println("Could not find a valid BMP180 sensor, check wiring!");
    while (1) {}
  }
   
}

void loop() {
    while (Serial2.available() > 0) {
        Serial2.read();
    }
    delay(2000); 
    String temperature = "";
    String humidity = "";

    temperature = Serial2.readStringUntil('\n');  // Read first line
    temperature.trim();

    humidity = Serial2.readStringUntil('\n');  // Read second line
    humidity.trim();


    float p = bmp.readPressure();
    String pressure = String(p);

    float a = bmp.readAltitude();
    String altitude = String(a);

  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(serverURL);
    http.addHeader("Content-Type", "application/json");
   
    String jsonPayload = "{\"temperature\":" + temperature +
                         ",\"humidity\":" + humidity + 
                         ",\"pressure\":" + pressure + 
                         ",\"altitude\":" + altitude + 
                         "}";
    Serial.println(jsonPayload);
    int httpResponseCode = http.POST(jsonPayload);
    Serial.println("HTTP Response code: " + String(httpResponseCode));

    String response = http.getString();
    Serial.println("Server response: " + response);

      
    if (httpResponseCode > 0) {
      String response = http.getString();
      Serial.println(httpResponseCode);
      Serial.println(response);
    } else {
      Serial.print("Error in sending POST: ");
      Serial.println(http.errorToString(httpResponseCode).c_str());
    }

    http.end();
  }
  
}

Am implementat un server in Python folosind Flask, aceasta fiind partea de backend.


from flask import Flask, request, jsonify, render_template
from datetime import datetime
 
app = Flask(__name__)

latest_data = {
    "temperature": None,
    "humidity": None,
    "pressure": None,
    "timestamp": None
}

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/update', methods=['GET'])
def get_data():
    return jsonify(latest_data)

@app.route('/data', methods=['POST'])
def receive_data():
    data = request.get_json(force=True)
    if data:
        latest_data["temperature"] = float(data.get("temperature", 0))
        latest_data["humidity"] = float(data.get("humidity", 0))
        latest_data["pressure"] = float(data.get("pressure", 0))
        latest_data["timestamp"] = datetime.now().strftime("%H:%M:%S")
        return jsonify({"status": "success"}), 200
    return jsonify({"status": "fail"}), 400

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Pentru a vizualiza datele in mod real intr-un mod cat mai placut si interactiv, am imple,mentat si partea de frontend a serverului, in HTML si JavaScript.

HTML:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Sensor Dashboard</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming"></script>
    <script src="https://cdn.jsdelivr.net/npm/@bernaferrari/gauge-chart"></script>
    <script defer src="/static/script.js"></script>
    <style>
        body { font-family: sans-serif; text-align: center; padding: 20px; background: #f5f5f5; }
        h1 { color: #333; }
        .container { display: flex; flex-wrap: wrap; justify-content: center; gap: 20px; }
        .card { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); }
        canvas { max-width: 600px; }
        .gauge-container { width: 250px; height: 150px; margin: auto; }
    </style>
</head>
<body>
    <h1>🌡️ Real-Time Sensor Dashboard</h1>

    <div class="container">
        <div class="card">
            <h3>Temperature Gauge</h3>
            <div id="gauge-temp" class="gauge-container"></div>
            <p>Temp: <span id="temp">--</span> °C</p>
        </div>

        <div class="card">
            <h3>Humidity Gauge</h3>
            <div id="gauge-hum" class="gauge-container"></div>
            <p>Humidity: <span id="hum">--</span> %</p>
        </div>
    </div>

    <div class="container">
        <div class="card">
            <h3>Temperature - Last 10 Minutes</h3>
            <canvas id="chart-temp" height="100"></canvas>
        </div>

        <div class="card">
            <h3>Humidity - Last 10 Minutes</h3>
            <canvas id="chart-hum" height="100"></canvas>
        </div>
    </div>

    <div class="card">
        <h3>Pressure</h3>
        <p>📈 Pressure: <span id="pres">--</span> Pa</p>
        <p>⏱️ Time: <span id="time">--:--:--</span></p>
    </div>
</body>
</html>

JavaScript:

const tempSpan = document.getElementById('temp');
const humSpan = document.getElementById('hum');
const presSpan = document.getElementById('pres');
const timeSpan = document.getElementById('time');

const gaugeTemp = GaugeChart.gaugeChart(document.getElementById("gauge-temp"), {
  arcDelimiters: [0.3, 0.6, 1],
  arcColors: ["#00bfff", "#ffa500", "#ff0000"],
  arcLabels: ["Low", "Medium", "High"],
  rangeLabel: ["0°C", "50°C"],
  centralLabel: ""
});

const gaugeHum = GaugeChart.gaugeChart(document.getElementById("gauge-hum"), {
  arcDelimiters: [0.3, 0.6, 1],
  arcColors: ["#add8e6", "#00ced1", "#007bff"],
  arcLabels: ["Dry", "Moderate", "Humid"],
  rangeLabel: ["0%", "100%"],
  centralLabel: ""
});

const ctxTemp = document.getElementById('chart-temp').getContext('2d');
const ctxHum = document.getElementById('chart-hum').getContext('2d');

const tempChart = new Chart(ctxTemp, {
  type: 'line',
  data: {
    datasets: [{
      label: 'Temperature (°C)',
      borderColor: 'rgba(255, 99, 132, 1)',
      backgroundColor: 'rgba(255, 99, 132, 0.2)',
      data: []
    }]
  },
  options: {
    plugins: {
      streaming: {
        frameRate: 30
      }
    },
    scales: {
      x: {
        type: 'realtime',
        realtime: {
          duration: 600000,
          refresh: 2000,
          delay: 2000,
          onRefresh: async chart => {
            await fetchAndUpdate(chart, 'temperature');
          }
        }
      },
      y: {
        beginAtZero: true
      }
    }
  }
});

const humChart = new Chart(ctxHum, {
  type: 'line',
  data: {
    datasets: [{
      label: 'Humidity (%)',
      borderColor: 'rgba(54, 162, 235, 1)',
      backgroundColor: 'rgba(54, 162, 235, 0.2)',
      data: []
    }]
  },
  options: {
    plugins: {
      streaming: {
        frameRate: 30
      }
    },
    scales: {
      x: {
        type: 'realtime',
        realtime: {
          duration: 600000,
          refresh: 2000,
          delay: 2000,
          onRefresh: async chart => {
            await fetchAndUpdate(chart, 'humidity');
          }
        }
      },
      y: {
        beginAtZero: true
      }
    }
  }
});

async function fetchAndUpdate(chart, type) {
  try {
    const res = await fetch('/update');
    const data = await res.json();
    const now = Date.now();

    if (type === 'temperature') {
      chart.data.datasets[0].data.push({ x: now, y: data.temperature });
    } else if (type === 'humidity') {
      chart.data.datasets[0].data.push({ x: now, y: data.humidity });
    }

    // Update UI
    tempSpan.textContent = data.temperature?.toFixed(1) ?? '--';
    humSpan.textContent = data.humidity?.toFixed(1) ?? '--';
    presSpan.textContent = data.pressure ?? '--';
    timeSpan.textContent = data.timestamp ?? '--';

    if (data.temperature != null) {
      gaugeTemp.update({ percent: Math.min(data.temperature / 50, 1) });
    }
    if (data.humidity != null) {
      gaugeHum.update({ percent: Math.min(data.humidity / 100, 1) });
    }
  } catch (err) {
    console.error("Fetch error:", err);
  }
}

Rezultate Obţinute

Rezultatele obtinute in acest proiect se pot vedea pe urmatorul link: https://youtu.be/XK-8px-DiMA?feature=shared

Concluzii

Bibliografie/Resurse

Listă cu documente, datasheet-uri, resurse Internet folosite, eventual grupate pe Resurse Software şi Resurse Hardware.

Export to PDF

pm/prj2025/fstancu/gabriel.nitu0912.1748383983.txt.gz · Last modified: 2025/05/28 01:13 by gabriel.nitu0912
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0