Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:prj2025:fstancu:gabriel.nitu0912 [2025/05/18 23:57]
gabriel.nitu0912
pm:prj2025:fstancu:gabriel.nitu0912 [2025/05/28 01:26] (current)
gabriel.nitu0912 [Concluzii]
Line 1: Line 1:
-====== Remote Weather Station ======+====== Remote Weather Station ​- NIŢU Gabriel ​======
 ===== Introducere ===== ===== 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. 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ă ===== ===== Descriere generală =====
  
 Schema bloc Schema bloc
  
 +{{ :​pm:​prj2025:​fstancu:​screenshot_2025-05-19_001618.png?​800 |}}
  
 ===== Hardware Design ===== ===== Hardware Design =====
- 
- 
  
   * Listă de piese   * Listă de piese
Line 24: Line 23:
 | Level Shifter cu 2 canale | 2 | https://​www.optimusdigital.ro/​ro/​interfata-convertoare-de-niveluri/​12562-convertor-de-nivel-logic-cu-2-canale-33v-5v-ttl.html?​search_query=convertor+de+nivel&​results=32 | | Level Shifter cu 2 canale | 2 | https://​www.optimusdigital.ro/​ro/​interfata-convertoare-de-niveluri/​12562-convertor-de-nivel-logic-cu-2-canale-33v-5v-ttl.html?​search_query=convertor+de+nivel&​results=32 |
 | Breadboard | 1 | https://​www.optimusdigital.ro/​ro/​prototipare-breadboard-uri/​44-breadboard-400-points.html?​search_query=breadboard&​results=125 | | Breadboard | 1 | https://​www.optimusdigital.ro/​ro/​prototipare-breadboard-uri/​44-breadboard-400-points.html?​search_query=breadboard&​results=125 |
 + 
 +Schema electrica
 +{{ :​pm:​prj2025:​fstancu:​schematic_savcxsa_2025-05-19_1_.png?​800 |}}
  
 +Cablajul
 +{{ :​pm:​prj2025:​fstancu:​whatsapp_image_2025-05-28_at_00.41.43_fa6385cf.jpg?​500 |}}
  
  
Line 30: Line 34:
  
  
-<note tip> +Mediul ​de dezvoltare:
-Descrierea codului aplicaţiei (firmware):​ +
-  * mediu de dezvoltare ​(if any) (e.g. AVR Studio, CodeVisionAVR) +
-  * librării şi surse 3rd-party (e.g. Procyon AVRlib) +
-  * algoritmi şi structuri pe care plănuiţi să le implementaţi +
-  * (etapa 3) surse şi funcţii implementate +
-</​note>​+
  
-===== Rezultate Obţinute =====+  * Arduino IDE 
 +  * VS Code
  
-<note tip> +Pe Arduino am implementat citirea senzorului DHT si trimiterea informatiilor catre ESP32
-Care au fost rezultatele obţinute în urma realizării proiectului vostru+<code> 
-</note>+SoftwareSerial espSerial(rxPin,​ txPin);
  
-===== Concluzii =====+void setup() { 
 +  pinMode(rxPin,​ INPUT); 
 +  pinMode(txPin,​ OUTPUT); 
 +     
 +  Serial.begin(9600);​ 
 +  espSerial.begin(9600);​ 
 +  dht.begin();​ 
 +}
  
-===== Download =====+void loop() { 
 +  Serial.println("​Sending to ESP32..."​);​ 
 +  int16_t ​ value 1234;
  
-<note warning> 
-O arhivă (sau mai multe dacă este cazul) cu fişierele obţinute în urma realizării proiectului:​ surse, scheme, etc. Un fişier README, un ChangeLog, un script de compilare şi copiere automată pe uC crează întotdeauna o impresie bună ;-). 
  
-Fişierele se încarcă pe wiki folosind facilitatea **Add Images or other files**Namespace-ul în care se încarcă fişierele este de tipul **:​pm:​prj20??:​c?​** sau **:​pm:​prj20??:​c?:​nume_student** ​(dacă este cazul). **Exemplu:​** Dumitru Alin, 331CC -> **:​pm:​prj2009:​cc:​dumitru_alin**. +  float humidity = dht.readHumidity(); 
-</​note>​+  float temperature = dht.readTemperature();​
  
-===== Jurnal =====+  if (isnan(humidity) || isnan(temperature)) { 
 +    Serial.println("​Failed to read from DHT sensor!"​);​ 
 +    return; 
 +  }
  
-<note tip+  espSerial.println(temperature);​ 
-Putețavea șo secțiune ​de jurnal în care să poată urmări asistentul ​de proiect progresul proiectului+  espSerial.println(humidity);​ 
-</note>+ 
 +  Serial.print("​Humidity:​ "); 
 +  Serial.print(humidity);​ 
 +  Serial.print("​ %\t"​);​ 
 +   
 +  Serial.print("​Temperature:​ "); 
 +  Serial.print(temperature);​ 
 +  Serial.println("​ *C"​);​ 
 + 
 +  delay(2000);​ 
 +
 +</code
 + 
 +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. 
 +<​code>​ 
 + 
 +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 = 0; < 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();​ 
 +  } 
 +   
 +
 +</​code>​ 
 + 
 +Am implementat un server in Python folosind Flask, aceasta fiind partea ​de backend. 
 +<​code>​ 
 + 
 + 
 +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) 
 +</​code>​ 
 + 
 + 
 +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: 
 +<​code>​ 
 +<​!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>​ 
 + 
 +</​code>​ 
 + 
 +JavaScript:​ 
 +<​code>​ 
 +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); 
 +  } 
 +
 + 
 +</​code>​ 
 + 
 + 
 +===== Rezultate Obţinute ===== 
 + 
 +Rezultatele obtinute in acest proiect se pot vedea pe urmatorul link: https://​youtu.be/​XK-8px-DiMA?​feature=shared 
 + 
 +===== Concluzii =====
  
 +Proiectul Remote Weather Station demonstreaza o solutie eficienta si accesibila pentru monitorizarea conditiilor meteorologice in timp real. Am invatat foarte multe lucruri lucrand la acest proiect, de la cum legi toate perifericele si le conectezi intre ele, pana la ce cod scrii pentru a face ceea ce iti propui. ​
 ===== Bibliografie/​Resurse ===== ===== Bibliografie/​Resurse =====
  
-<​note>​ +Resurse hardware 
-Listă cu documente, datasheet-uri, resurse Internet folosite, eventual grupate pe **Resurse Software** şi **Resurse Hardware**. +  * Datasheet BME280 (Bosch): https://​www.bosch-sensortec.com/​products/​environmental-sensors/​humidity-sensors-bme280/​ 
-</note>+  ​Datasheet DHT22: https://​cdn.sparkfun.com/​assets/​f/​7/​d/​9/​c/​DHT22.pdf 
 +  * Datasheet ESP32: ​ https://​www.espressif.com/​sites/​default/​files/​documentation/​esp32-wroom-32_datasheet_en.pdf
  
-<​html><​a class="​media mediafile mf_pdf"​ href="?​do=export_pdf">​Export to PDF</​a></​html>​ 
  
 +Resurse software
 +  * Documentație oficială Arduino: https://​www.arduino.cc/​
 +  *  Bibliotecă Adafruit Sensor: https://​github.com/​adafruit/​Adafruit_Sensor
 +  * Connect ESP32 to wifi: https://​www.electronicwings.com/​esp32/​esp32-wi-fi-basics-getting-started
 +  * Biblioteca Wifi: https://​docs.arduino.cc/​libraries/​wifi/​
pm/prj2025/fstancu/gabriel.nitu0912.1747601859.txt.gz · Last modified: 2025/05/18 23:57 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