GrowSimple

GrowSimple

🎯 Στόχος, 1η φάση

Η συσκευή:

  1. Κοιμάται (deep sleep) για εξοικονόμηση ενέργειας
  2. Ξυπνάει κάθε 1 ώρα (ή πιο γρήγορα για δοκιμή)
  3. Παίρνει μετρήσεις από:
  • DHT11 (Θερμοκρασία, Υγρασία αέρα)
  • Light sensor (Φως)
  • Soil sensor (Υγρασία εδάφους)
  1. Κάθε φορά ενημερώνει και εμφανίζει:
  • τρέχον μέσο όρο (μέχρι στιγμής στον “κύκλο ημέρας”)
  • ελάχιστη τιμή
  • μέγιστη τιμή
  1. Αν πατηθεί ο touch sensor, η συσκευή:
  • ξυπνάει άμεσα
  • στέλνει (στη Φάση 1: τυπώνει) τα στατιστικά ημέρας (avg/min/max)
  • και μετά ξανακοιμάται

📌 “Ημέρα” στη Φάση 1 = 24 μετρήσεις. Μετά μηδενίζει και ξεκινά νέο κύκλο.


🧰 Υλικά

  • FireBeetle 2 ESP32-C6
  • DHT11 (Acebott module)
  • DFRobot Ambient Light Sensor (DFR0026)
  • DFRobot Capacitive Soil Moisture (SEN0193)
  • DFRobot Capacitive Touch Sensor (DFR0030)
  • breadboard / perfboard
  • καλώδια Dupont
  • USB-C

🔌 Συνδεσμολογία (όλα μαζί)

Τροφοδοσία

  • Όλοι οι αισθητήρες: VCC → 3.3V, GND → GND

Pins δεδομένων (όπως έχουμε ορίσει)

ΑισθητήραςPin
DHT11 DATAGPIO15 (D13)
Light AOGPIO2 (A1 / ADC)
Soil AOGPIO3 (A2 / ADC)
Touch OUT (wake)GPIO6 (D12 / LP GPIO)

💻 Προετοιμασία Arduino IDE

  1. Board: ESP32 by Espressif
  2. Επιλογή board: FireBeetle 2 ESP32-C6
  3. Library Manager: εγκατάσταση
  • DHT sensor library (Adafruit)
  • Adafruit Unified Sensor (αν ζητηθεί)

🧑‍💻 Κώδικας (Deep Sleep + 24-cycle stats + Touch send)

✅ Για δοκιμή στο εργαστήριο, ΔΕΝ περιμένουμε 1 ώρα.
Θα βάλουμε προσωρινά π.χ. 10 δευτερόλεπτα και μετά το γυρίζουμε σε 3600s.

#include <DHT.h>
#include <Preferences.h>
#include <esp_sleep.h>
#include <math.h>

// ---------------- Pins ----------------
#define DHTPIN     15
#define DHTTYPE    DHT11

#define LIGHT_PIN  2   // A1 / ADC
#define SOIL_PIN   3   // A2 / ADC
#define TOUCH_PIN  6   // LP GPIO -> wake on touch

// ---------------- Sleep interval ----------------
// Για δοκιμή στο εργαστήριο βάλε 10 ή 30.
// Για κανονική λειτουργία: 3600 (1 ώρα).
#define WAKE_INTERVAL_SECONDS 10

// ---------------- Day cycle ----------------
#define SAMPLES_PER_DAY 24

// ---------------- Globals ----------------
DHT dht(DHTPIN, DHTTYPE);
Preferences prefs;

// Δομή στατιστικών (θα αποθηκεύεται σε NVS)
struct Stats {
  uint8_t count;

  float t_sum, h_sum;
  int   light_sum, soil_sum;

  float t_min, t_max;
  float h_min, h_max;
  int   light_min, light_max;
  int   soil_min,  soil_max;
};

Stats st;

// ---------------- Helpers ----------------
void resetStats() {
  st.count = 0;

  st.t_sum = 0; st.h_sum = 0;
  st.light_sum = 0; st.soil_sum = 0;

  st.t_min =  9999; st.t_max = -9999;
  st.h_min =  9999; st.h_max = -9999;
  st.light_min =  999999; st.light_max = -999999;
  st.soil_min  =  999999; st.soil_max  = -999999;
}

void loadStats() {
  prefs.begin("envstats", true); // read-only
  size_t len = prefs.getBytesLength("stats");
  prefs.end();

  if (len == sizeof(Stats)) {
    prefs.begin("envstats", true);
    prefs.getBytes("stats", &st, sizeof(Stats));
    prefs.end();
  } else {
    resetStats();
  }
}

void saveStats() {
  prefs.begin("envstats", false);
  prefs.putBytes("stats", &st, sizeof(Stats));
  prefs.end();
}

void updateMinMaxFloat(float v, float &vmin, float &vmax) {
  if (v < vmin) vmin = v;
  if (v > vmax) vmax = v;
}

void updateMinMaxInt(int v, int &vmin, int &vmax) {
  if (v < vmin) vmin = v;
  if (v > vmax) vmax = v;
}

void printStats(const char* title) {
  Serial.println();
  Serial.println(title);
  Serial.print("Μετρήσεις σήμερα: ");
  Serial.print(st.count);
  Serial.print("/");
  Serial.println(SAMPLES_PER_DAY);

  if (st.count == 0) {
    Serial.println("Δεν υπάρχουν ακόμα δεδομένα.");
    return;
  }

  float t_avg = st.t_sum / st.count;
  float h_avg = st.h_sum / st.count;
  float light_avg = (float)st.light_sum / st.count;
  float soil_avg  = (float)st.soil_sum / st.count;

  Serial.print("Θερμοκρασία  avg/min/max: ");
  Serial.print(t_avg, 1); Serial.print(" / ");
  Serial.print(st.t_min, 1); Serial.print(" / ");
  Serial.print(st.t_max, 1); Serial.println(" °C");

  Serial.print("Υγρασία αέρα avg/min/max: ");
  Serial.print(h_avg, 1); Serial.print(" / ");
  Serial.print(st.h_min, 1); Serial.print(" / ");
  Serial.print(st.h_max, 1); Serial.println(" %");

  Serial.print("Φως          avg/min/max: ");
  Serial.print(light_avg, 0); Serial.print(" / ");
  Serial.print(st.light_min); Serial.print(" / ");
  Serial.println(st.light_max);

  Serial.print("Υγρασία εδάφους avg/min/max: ");
  Serial.print(soil_avg, 0); Serial.print(" / ");
  Serial.print(st.soil_min); Serial.print(" / ");
  Serial.println(st.soil_max);
}

// Placeholder: στη Φάση 2 εδώ θα μπει BLE send προς app
void sendToApp() {
  Serial.println();
  Serial.println("📤 Αποστολή στην εφαρμογή (Φάση 1: Serial output)...");
  printStats("📊 Στατιστικά ημέρας (κύκλος 24 μετρήσεων)");
}

// ---------------- Setup sleep ----------------
void goToSleep() {
  // wake από timer
  esp_sleep_enable_timer_wakeup((uint64_t)WAKE_INTERVAL_SECONDS * 1000000ULL);

  // wake από touch (pin HIGH)
  esp_sleep_enable_ext0_wakeup((gpio_num_t)TOUCH_PIN, 1);

  Serial.println("😴 Deep sleep...");
  Serial.flush();
  esp_deep_sleep_start();
}

// ---------------- Main ----------------
void setup() {
  Serial.begin(115200);
  delay(300);

  dht.begin();
  loadStats();

  esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();

  // Αν ξύπνησε από touch -> στείλε stats, μείνε λίγο “available”, μετά sleep
  if (cause == ESP_SLEEP_WAKEUP_EXT0) {
    Serial.println("👆 Ξύπνησα από TOUCH!");
    sendToApp();

    // μικρό “παράθυρο” για app σύνδεση στη Φάση 2
    delay(5000);

    goToSleep();
    return;
  }

  // Αν ξύπνησε από timer (ή power-on) -> πάρε μετρήσεις και ενημέρωσε stats
  if (cause == ESP_SLEEP_WAKEUP_TIMER) {
    Serial.println("⏰ Ξύπνησα από TIMER (ώρα για μέτρηση)!");
  } else {
    Serial.println("🔌 Εκκίνηση (power on / reset).");
  }

  // Αν ολοκληρώθηκαν 24 μετρήσεις, ξεκινά νέα “ημέρα”
  if (st.count >= SAMPLES_PER_DAY) {
    Serial.println("🗓️ Νέος κύκλος ημέρας (reset στατιστικών).");
    resetStats();
  }

  // Διαβάζουμε αισθητήρες
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  int lightVal = analogRead(LIGHT_PIN);
  int soilVal  = analogRead(SOIL_PIN);

  // Έλεγχος DHT
  if (isnan(t) || isnan(h)) {
    Serial.println("⚠️ Σφάλμα ανάγνωσης DHT11. Δεν ενημερώνω stats αυτή τη φορά.");
    // Παρ’ όλα αυτά κοιμήσου και δοκίμασε στην επόμενη αφύπνιση
    goToSleep();
    return;
  }

  // Ενημέρωση stats
  st.count++;

  st.t_sum += t;
  st.h_sum += h;
  st.light_sum += lightVal;
  st.soil_sum  += soilVal;

  updateMinMaxFloat(t, st.t_min, st.t_max);
  updateMinMaxFloat(h, st.h_min, st.h_max);
  updateMinMaxInt(lightVal, st.light_min, st.light_max);
  updateMinMaxInt(soilVal,  st.soil_min,  st.soil_max);

  saveStats();

  // Εμφάνιση “τρέχοντος” μέσου όρου + min/max
  printStats("✅ Νέα μέτρηση καταγράφηκε. Τρέχοντα στατιστικά ημέρας:");

  // Sleep ξανά
  goToSleep();
}

void loop() {
  // Δεν θα εκτελεστεί (μπαίνουμε πάντα σε deep sleep)
}

▶️ Δοκιμή στο εργαστήριο (χωρίς να περιμένουμε 1 ώρα)

  1. Βάζουμε προσωρινά:
    • #define WAKE_INTERVAL_SECONDS 10
  2. Upload
  3. Serial Monitor: 115200
  4. Θα δεις κύκλο:
    • ξύπνα → μέτρηση → stats → deep sleep
    • κάθε ~10s μια νέα “ώρα”
  5. Αγγίζεις τον touch sensor:
    • ξυπνάει αμέσως
    • τυπώνει “Αποστολή στην εφαρμογή (Φάση 1: Serial output)”
    • δείχνει avg/min/max
  6. Όταν επιβεβαιωθεί ότι δουλεύει, αλλάζεις:
    • WAKE_INTERVAL_SECONDS 3600

✅ Τι πετύχαμε

  • Στήσαμε σύστημα χαμηλής κατανάλωσης
  • Φτιάξαμε στατιστικά ημέρας χωρίς να κρατάμε “όλες τις τιμές”
  • Βάλαμε δύο τρόπους αφύπνισης:
    • timer wake
    • touch wake (manual “send”)

🧩 Σημειώσεις

  • Στη Φάση 1 “ημέρα” = 24 μετρήσεις.
  • Τα δεδομένα δεν χάνονται στο deep sleep, γιατί αποθηκεύονται σε NVS (Preferences).

🚀 Φάση 2 (όταν θα βάλετε RTC)

Τότε θα κάνουμε:

  • πραγματική ημερομηνία/ώρα
  • reset στατιστικών όταν αλλάζει η ημερολογιακή μέρα
  • (προαιρετικό) timestamp σε κάθε μέτρηση
  • Αποστολή μέσω BLE στο κινητό

Σχόλια

Δεν υπάρχουν ακόμη σχόλια. Γιατί δεν ξεκινάτε τη συζήτηση;

Αφήστε μια απάντηση