Arduino uno R4 hängt sich regelmäßig (48h) auf, Hydroponic

Hallo! Folgendes Problem: Ich habe mir eine Anlage zur Steuerung eines Hydroponic-System gebaut. Leider setzt mein Code ca alle 48h immer so zu 5:20 aus. Was das Programmieren betrifft bin ich noch recht neu, viele Dinge sind mit Chat Gpt ausgeholfen. Ich komme aber auch nach X-Versuchen nicht dahinter wiso das Problem auftritt.

Hier einmal mein Code:

#include <DFRobot_EC.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "DHT.h"
#include <DFRobot_PH.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <TimeLib.h>
#include "thingProperties.h"
 /* 
  Sketch generated by the Arduino IoT Cloud Thing "Untitled"
  https://create.arduino.cc/cloud/things/f7f1eb64-f202-4b5a-8881-c2c96c09ce10 

  Arduino IoT Cloud Variables description

  The following variables are automatically generated and updated when changes are made to the Thing

  float ec_act;
  float ec_offset;
  float flowRate;
  float humi;
  float next_Cycle;
  float ph_act;
  float ph_minus_est_vol;
  float ph_offset;
  float ph_plus_est_vol;
  float pH_setpoint;
  float pH_threshold;
  float tempC;
  float time_pump_cycle;
  float volume;
  float wtempC;
  int abc_mix;
  int light_time;
  bool air_pump;
  bool a_pump;
  bool automatic;
  bool b_pump;
  bool c_pump;
  bool ec_fill;
  bool humiditer;
  bool light;
  bool pH_minus_pump;
  bool pH_plus_pump;
  bool reset;
  bool status_dosinglevel;
  bool status_humiditer;
  bool status_light;
  bool status_mixing_pump;
  bool status_oxygen;
  bool status_ph_minus;
  bool status_ph_plus;
  bool status_water;
  bool water_pump;

  Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
  which are called when their values are changed from the Dashboard.
  These functions are generated with the Thing and added at the end of this sketch.
*/
// ####################################################################
// Define pins
#define PH_PIN A0
#define EC_PIN A1
#define DHT22_PIN A2
#define ONE_WIRE_BUS 5
//Flowsensor_PIN 2

#define RELAYLight_PIN 4 
#define RELAYHUM_PIN 3
#define RELAYWater_PIN 6 
#define RELAYO2_PIN 7
#define RELAYMix_PIN 8

#define RELAYA_PIN 9
#define RELAYB_PIN 10
#define RELAYC_PIN 11
#define RELAYpHm_PIN 12
#define RELAYpHp_PIN 1
// ####################################################################
//Module
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DHT dht22(DHT22_PIN, DHT22);
DFRobot_PH ph;
DFRobot_EC ec;
LiquidCrystal_I2C lcd(0x27, 20, 4);
// ####################################################################
// Multiplexer
const uint8_t TCA9548A_ADDR = 0x70; 
const uint8_t LCD_CHANNEL = 0;
// ####################################################################
// pH
float ph_voltage, ph_value, ph_temp, sum_ph, doseTime_pH_plus, doseTime_pH_minus;
float phReadings[5];
int count_ph_minus = 0;
int count_ph_plus = 0;
unsigned long start_pH_minus = 0, start_pH_plus = 0; 
// ####################################################################
// EC
float ec_voltage, ec_value, ec_temp, sum_ec;
unsigned long dose_A, dose_B, dose_C;
float ecReadings[5];
float vol_A, vol_B, vol_C, level_A, level_B, level_C;
int count_A = 0;
int count_B = 0;
int count_C = 0;
unsigned long start_RelayA = 0, start_RelayB = 0, start_RelayC = 0;
bool relayAState = true, relayBState = true, relayCState = true;
// ####################################################################
//Mixing_pump
unsigned long start_MixingPumpCheck = 0; 
const unsigned long mixingPumpInterval = 180000;
// ####################################################################
//Control
unsigned long CheckControl = 0;
const unsigned long ControlInterval = 60000;
// ####################################################################
//flowrate
volatile int pulseCount;  
const int sensorPin = 2;
// ####################################################################
//NTPC
WiFiUDP ntpUDP; 
NTPClient timeClient(ntpUDP, "pool.ntp.org", 3600, 60000);  // UTC+1
unsigned long unixTime, lastCheckWateron, lastCheckWateroff, lastCheckO2on, lastCheckHumoff, lastCheckHumon;
// ####################################################################
//Light
bool should_light_be_on;
int light_on_hours;
int light_on_minutes;
int light_off_hours;
int light_off_minutes;
// ####################################################################
//pump
float pump_cycle;
const int pump_duration = 900; // 15 min
bool manual;
// ####################################################################
//Humi
const unsigned long humiinterval= 14400; // 2 h
// ####################################################################
// Calibration
static char inputBuffer[50];
int seconds = 0;  
unsigned long startTime = millis();
// ####################################################################
//Sub
void Sensor_read();
void Adjust();
void level();
void Relay_stop();
void Light();
void Watering();
void Humi();
void controlRelaymix_on();
void controlRELAYWater_on();
void controlRELAYWater_off();
void controlRELAYO2_on();
void controlRELAYO2_off();
void cloudinput();
void pulse();
void cali_menu();
void dead_cal();
void tca9548a_select(uint8_t channel);
// ####################################################################
void setup(){
  //Serial&Sensors  
  Serial.begin(9600);
  sensors.begin();
  dht22.begin();
  Wire.begin();
  ph.begin();
  ec.begin();
  
  //Flowrate
  pulseCount = 0;
  volume = 0;  
  attachInterrupt(digitalPinToInterrupt(sensorPin), pulse, RISING);  // DIGITAL Pin 2: Interrupt 0

  // Initializing LCD
  tca9548a_select(LCD_CHANNEL);
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing...");
  lcd.setCursor(0, 2);
  lcd.print("Hydrobox-System");
  
  //Relais
  pinMode(RELAYLight_PIN, OUTPUT);
  pinMode(RELAYWater_PIN, OUTPUT);
  pinMode(RELAYO2_PIN, OUTPUT);
  pinMode(RELAYMix_PIN, OUTPUT);
  pinMode(RELAYHUM_PIN, OUTPUT);
  
  pinMode(RELAYpHm_PIN, OUTPUT);
  pinMode(RELAYpHp_PIN, OUTPUT);
  pinMode(RELAYA_PIN, OUTPUT);
  pinMode(RELAYB_PIN, OUTPUT);
  pinMode(RELAYC_PIN, OUTPUT);
  
  digitalWrite(RELAYLight_PIN, HIGH);
  digitalWrite(RELAYWater_PIN, HIGH);
  digitalWrite(RELAYO2_PIN, HIGH);
  digitalWrite(RELAYMix_PIN, HIGH);
  digitalWrite(RELAYHUM_PIN, HIGH);
  
  digitalWrite(RELAYpHm_PIN, HIGH);
  digitalWrite(RELAYpHp_PIN, HIGH);
  digitalWrite(RELAYA_PIN, HIGH);
  digitalWrite(RELAYB_PIN, HIGH);
  digitalWrite(RELAYC_PIN, HIGH);

  Serial.println("Initializing  Relais");
  tca9548a_select(LCD_CHANNEL);
  delay(1000); 
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing Relais");
  delay(1000);

  //Status reset
  automatic = false;
  manual = false;
  status_dosinglevel = false;
  status_light = false;
  status_mixing_pump = false;
  status_oxygen = false;
  status_ph_minus = false;
  status_ph_plus = false;
  status_water = false;
  status_humiditer = false;  
  relayAState = false;  
  relayBState = false; 
  relayCState = false;
  ec_fill = false;
  pump_cycle = 3600;
  
  Serial.println("Initializing  Status");
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing Status");
  delay(1000);

  //Connect to Arduino IoT Cloud
  initProperties();
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and   errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
 */
  Serial.println("Connecting to Arduino IoT Cloud...");
  unsigned long cloudStartTime = millis();
  while (!ArduinoCloud.connected()) {
    ArduinoCloud.update();
    delay(500);
    if (millis() - cloudStartTime > 30000) { // Timeout nach 30 Sekunden
      Serial.println("Cloud connection timeout.");
      break;
    }
  }

  if (ArduinoCloud.connected()) {
    Serial.println("Connected to Arduino IoT Cloud!");
  } else {
    Serial.println("Failed to connect to IoT Cloud.");
  }
   
  tca9548a_select(LCD_CHANNEL);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing Cloud");
  delay(5000);
  
  //NTPC
  if (WiFi.status() == WL_CONNECTED) {
    timeClient.begin();
    timeClient.update();
    unsigned long unixTime = timeClient.getEpochTime();
    setTime(unixTime);
    Serial.print("Current time: ");
    Serial.println(unixTime);
  } else {
    Serial.println("WiFi not connected, skipping NTP initialization.");
  }

  lastCheckWateron = unixTime;
  lastCheckO2on = unixTime;
  lastCheckWateroff = unixTime;
  lastCheckHumoff = unixTime;
  lastCheckHumon = unixTime;
  
  tca9548a_select(LCD_CHANNEL);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing NTPC");
  delay(1000);

  Serial.println("Initializing  finished"); 
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing End");
  delay(1000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Loading Sensordata");
  Serial.println("Loading Sensordata");
}
// ####################################################################
void loop() {
//Update
  ArduinoCloud.update(); 
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  
//Control
  if ((unsigned long)(millis() - CheckControl) >= ControlInterval) {
    Sensor_read();
    Adjust();
    level();
    cloudinput();
    
    timeClient.update(); 
    unixTime = timeClient.getEpochTime();
    setTime(unixTime);
    
    Serial.print("Current Time: ");
    Serial.print(hour());
    Serial.print(":");
    if (minute() < 10) {
      Serial.print("0");
    }
    Serial.print(minute());
    Serial.print(":");
    if (second() < 10) {
      Serial.print("0");
    }
    Serial.println(second());
   
    pulseCount = 0;
    CheckControl = millis();
  }
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//TIMER
Light();
Watering();
Humi();
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//Relay stop
  if (status_ph_minus || status_ph_plus || relayAState || relayBState || relayCState || status_mixing_pump){
     Relay_stop(); 
  } 
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 if (Serial.available() > 0) {
        size_t len = Serial.readBytesUntil('\n', inputBuffer, sizeof(inputBuffer) - 1);
        inputBuffer[len] = '\0'; 

        if (strcasecmp(inputBuffer, "cal") == 0) {
            cali_menu();
        } else if (strcasecmp(inputBuffer, "dead") == 0) {
            dead_cal();
        } else {
            Serial.println("Incorrect input");
        }
    }  
delay(50);
}
// ##################################################################################
void tca9548a_select(uint8_t channel) {
    if (channel > 7) return;
    Wire.beginTransmission(TCA9548A_ADDR);
    Wire.write(1 << channel);
    Wire.endTransmission();
}
// ##################################################################################
void pulse(){
  pulseCount++;
}
// ##################################################################################
void dead_cal() {
    //pH -
    doseTime_pH_minus = 6.0 * 352.4; // 6.0 mL
    start_pH_minus = millis();       
    status_ph_minus = true;   
    digitalWrite(RELAYpHm_PIN, LOW);
    //pH+
    doseTime_pH_plus = 6.6 * 478.5; // 6.6 mL
    start_pH_plus = millis();        
    status_ph_plus = true;           
    digitalWrite(RELAYpHp_PIN, LOW);
    //A
    dose_A = 6.9 * 357; // 6.9 mL
    start_RelayA = millis();
    relayAState = true; 
    digitalWrite(RELAYA_PIN, LOW); 
    //B
    dose_B = 6.4 * 382; // 6.4 mL
    start_RelayB = millis();
    relayBState = true; 
    digitalWrite(RELAYB_PIN, LOW);
    //C
    dose_C = 6.3 * 379; // 6.3 mL/s 
    start_RelayC = millis();
    relayCState = true; 
    digitalWrite(RELAYC_PIN, LOW); 
}
// ##################################################################################
void cali_menu() {
    Serial.println("Calibration started. Type 'exit' to exit or 'help' for further commands.");
    showHelpMenu(); 
    bool running = true; 
    String input = "";   

    while (running) {
        if (Serial.available() > 0) {
            input = Serial.readStringUntil('\n');
            input.trim(); 

            if (input.equalsIgnoreCase("exit")) {
                Serial.println("Calibration completed.");
                Serial.println("Restart!");
                running = false;
                NVIC_SystemReset();
            } else if (input.equalsIgnoreCase("Pump A")) {
                handlePump(RELAYA_PIN, "Pump A");
            } else if (input.equalsIgnoreCase("Pump B")) {
                handlePump(RELAYB_PIN, "Pump B");
            } else if (input.equalsIgnoreCase("Pump C")) {
                handlePump(RELAYC_PIN, "Pump C");
            } else if (input.equalsIgnoreCase("Pump pH+")) {
                handlePump(RELAYpHp_PIN, "Pump pH+");
            } else if (input.equalsIgnoreCase("Pump pH-")) {
                handlePump(RELAYpHm_PIN, "Pump pH-");
            } else if (input.equalsIgnoreCase("help")) {
                showHelpMenu();
            } else {
                Serial.println("Unknown command. Type 'help' for available commands.");
            }

            input = ""; 
        }
    }
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void handlePump(int pin, String pumpName) {
    Serial.println(pumpName + " started");
    digitalWrite(pin, LOW);

    unsigned long startTime = millis();
    unsigned long elapsed = 0;
    while (elapsed < 10000) { // 10 Sec
        elapsed = millis() - startTime;
        if (elapsed % 1000 == 0) { 
            Serial.print("Seconds: ");
            Serial.println(elapsed / 1000);
        }
    }

    digitalWrite(pin, HIGH);
    Serial.println(pumpName + " stopped");
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void showHelpMenu() {
    Serial.println("Available commands:");
    Serial.println("Pump A");
    Serial.println("Pump B");
    Serial.println("Pump C");
    Serial.println("Pump pH+");
    Serial.println("Pump pH-");
    Serial.println("exit");
}
// ##################################################################################
void cloudinput(){
//Light_time
  if (light_time == 1) {
    light_on_hours = 6;  // 12h
    light_on_minutes = 0;
    light_off_hours = 18;
    light_off_minutes = 0;
  }
  if (light_time == 2) {
    light_on_hours = 6; // 15h
    light_on_minutes = 0;
    light_off_hours = 21;
    light_off_minutes = 0;
  }
  if (light_time == 3){ 
    light_on_hours = 10; // 6h
    light_on_minutes = 0;
    light_off_hours = 16;
    light_off_minutes = 0; 
  }
  if (light_time == 4){ 
    light_on_hours = 8; // 8h
    light_on_minutes = 0;
    light_off_hours = 16;
    light_off_minutes = 0; 
  }
  if (light_time == 5){ 
    light_on_hours = 7; // 10h
    light_on_minutes = 0;
    light_off_hours = 17;
    light_off_minutes = 0; 
  }
should_light_be_on = 
    (hour() > light_on_hours || (hour() == light_on_hours && minute() >= light_on_minutes)) &&
    (hour() < light_off_hours || (hour() == light_off_hours && minute() <= light_off_minutes));
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//pump-cycle
  pump_cycle = time_pump_cycle * 3600;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//next Cycle
  long timeSinceLastWateron = unixTime - lastCheckWateron;
  next_Cycle = round(pump_cycle - timeSinceLastWateron) / 60;
  if (next_Cycle < 0) {
    next_Cycle = 0;  
  }  
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  
//Nutri ~20L
   if (abc_mix == 0){
    vol_A = 10; // Grow Seed Tri-part
    vol_B = 10;
    vol_C = 10;
  }
  else if (abc_mix == 1){
    vol_A = 20; // Grow young Tri-part
    vol_B = 20;
    vol_C = 20;
  }
  else if (abc_mix == 2){
    vol_A = 36; // Grow aduld Tri-part
    vol_B = 24;
    vol_C = 12;
  }
  else if (abc_mix == 3){
    vol_A = 40; // Pre Bloom Tri-part
    vol_B = 40;
    vol_C = 30;
  }
  else if (abc_mix == 4){
    vol_A = 16; // Bloom Tri-part
    vol_B = 32;
    vol_C = 48;
  }
  else if (abc_mix == 5){
    vol_A = 0; // Riping Tri-part
    vol_B = 0;
    vol_C = 100;
  }
  else if (abc_mix == 6){
    vol_A = 50; // Masterblend 1/2
    vol_B = 50;
    vol_C = 0;
  }
  else if (abc_mix == 7){
    vol_A = 100; // Masterblend 2/2
    vol_B = 100;
    vol_C = 0; 
  } else {
    vol_A = 0; // Default values if abc_mix is invalid
    vol_B = 0;
    vol_C = 0;
    }
}
// ##################################################################################  
void Sensor_read() { 
  // DHT22                                     
  humi = dht22.readHumidity();
  tempC = dht22.readTemperature();
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  // DS18S20 
  sensors.requestTemperatures();
  wtempC = round(sensors.getTempCByIndex(0) * 100) / 100.0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  // pH
  sum_ph = 0;
  for (int i = 0; i < 5; i++) {
  ph_voltage = analogRead(PH_PIN) / 1024.0 * 5000;
  ph_value = ph.readPH(ph_voltage, wtempC);
  ph_temp = round((ph_value + ph_offset) * 100 ) / 100.0;
  phReadings[i] = ph_temp;
  sum_ph += ph_temp;
  }
  ph_act = round((sum_ph / 5.0) * 100) / 100;
  if (ph_act < 0) ph_act = 0;
  if (ph_act > 14) ph_act = 14;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  // EC
  sum_ec = 0;
  for (int i = 0; i < 5; i++) {
  ec_voltage = analogRead(EC_PIN) / 1024.0 * 5000;         
  ec_value = ec.readEC(ec_voltage, wtempC);  
  ec_temp = round(((ec_value + ec_offset) * 1000) * 100) / 100.0; // µs/cm 
  ecReadings[i] = ec_temp;
  sum_ec += ec_temp; 
  }
  ec_act = (long)(sum_ec / 5.0);
  if (ec_act < 0) ec_act = 0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  //Flow
    flowRate = (pulseCount / 450.0) * 60.0;
  if (flowRate > 0){
    volume += (flowRate);}
  if (flowRate == 0){
    volume = 0;}
  if (status_water == false){ 
  volume = 0;}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  // LCD Ausgabe
  tca9548a_select(LCD_CHANNEL);  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("HUM:"));
  lcd.setCursor(4, 0);
  lcd.print(humi);    

  lcd.setCursor(10, 0); 
  lcd.print(F("TEMP:"));  
  lcd.setCursor(15, 0);  
  lcd.print(tempC);  

  lcd.setCursor(0, 1); 
  lcd.print(F("WTEMP:"));  
  lcd.setCursor(6, 1);  
  lcd.print(wtempC);     

  lcd.setCursor(0, 2);
  lcd.print(F("Cycle in:"));
  lcd.setCursor(10, 2);
  lcd.print(next_Cycle);
  lcd.setCursor(14, 2);
  lcd.print("min");

  lcd.setCursor(0, 3);
  lcd.print(F("pH:"));
  lcd.setCursor(3, 3);
  lcd.print(ph_act);

  lcd.setCursor(10, 3);
  lcd.print(F("EC:"));
  lcd.setCursor(13, 3);
  lcd.print(ec_act);

  Serial.println("Sensors_read");
}
// ##################################################################################  
void Adjust (){
// pH > Setpoint
  if (ph_act > pH_setpoint + pH_threshold && !manual) {
       doseTime_pH_minus = (1.5) * 358.4; // 1 mL; 2,79 mL/s
      
       if (!status_ph_minus) { 
          start_pH_minus = millis();       
          status_ph_minus = true;   
          digitalWrite(RELAYpHm_PIN, LOW);  
          Serial.println("Pump pH- on");
          count_ph_minus++;
         }
       if (!status_mixing_pump){
          start_MixingPumpCheck = millis();
          status_mixing_pump = true; 
          digitalWrite(RELAYMix_PIN, LOW);
          Serial.println("Mixing_pump turned on");
         } 
        Serial.println("pH high");
     } 
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// pH < Setpoint
  if (ph_act < pH_setpoint - pH_threshold && !manual) {
      doseTime_pH_plus = (1.5) * 478.5; // 1 mL; 2,09 mL/s
       
     if (!status_ph_plus) { 
        start_pH_plus = millis();        
        status_ph_plus = true;           
        digitalWrite(RELAYpHp_PIN, LOW); 
        Serial.println("Pump pH+ on");
        count_ph_plus++;
        }
     if (!status_mixing_pump){
        start_MixingPumpCheck = millis();
        status_mixing_pump = true; 
        digitalWrite(RELAYMix_PIN, LOW);
        Serial.println("Mixing_pump turned on");
        } 
        Serial.println("pH low");
    } 
  Serial.println("Adjusted");
}
// ################################################################################## 
void level() { 
  //Correction Agent level 
  ph_minus_est_vol = 100 - (count_ph_minus * (1.5));
  ph_plus_est_vol  = 100 - (count_ph_plus * (1.5));
  if (ph_minus_est_vol <= 5 || ph_plus_est_vol <= 5){
  status_dosinglevel = true;
  }
  //Nutri level
  level_A = 100 - (count_A * vol_A);
  level_B = 100 - (count_B * vol_A);
  level_C = 100 - (count_C * vol_A);
  if (level_A <= 5 || level_B <= 5 || level_C <= 5){
  status_dosinglevel = true;
  }
}
// ##################################################################################
void Relay_stop() {
  //Peristaltic Relays
   if (status_ph_minus == true && ((unsigned long)(millis() - start_pH_minus) >= doseTime_pH_minus)) {
    digitalWrite(RELAYpHm_PIN, HIGH);      
    status_ph_minus = false; 
    Serial.println("Pumpe ph- aus");
  }
   if (status_ph_plus == true && ((unsigned long)(millis() - start_pH_plus) >= doseTime_pH_plus)) {
    digitalWrite(RELAYpHp_PIN, HIGH);       
    status_ph_plus = false;    
    Serial.println("Pumpe ph+ aus");
  }
    if (relayAState == true && ((unsigned long)(millis() - start_RelayA) >= dose_A )) {
    digitalWrite(RELAYA_PIN, HIGH); 
    relayAState = false; 
    Serial.println("Pumpe A aus");
  }
    if (relayBState == true && ((unsigned long)(millis() - start_RelayB) >= dose_B )) {
    digitalWrite(RELAYB_PIN, HIGH); 
    relayBState = false;  
    Serial.println("Pumpe B aus");
  }
    if (relayCState == true && ((unsigned long)(millis() - start_RelayC) >= dose_C )) {
    digitalWrite(RELAYC_PIN, HIGH); 
    relayCState = false; 
    Serial.println("Pumpe C aus");
  }
  //Mixing pump
  if ((unsigned long)(millis() - start_MixingPumpCheck) >= mixingPumpInterval && status_mixing_pump == true) {
    digitalWrite(RELAYMix_PIN, HIGH); 
    status_mixing_pump = false;
    Serial.println("Mixing_pump turned off");
  }
}
// ##################################################################################   
void controlRELAYWater_on(){
 if(!manual){
  status_water = true;
  digitalWrite(RELAYWater_PIN, LOW);
  Serial.println("Water_pump turned on");
  }      
}
// ##################################################################################
void controlRELAYWater_off(){
 if(!manual){
  digitalWrite(RELAYWater_PIN, HIGH);
  status_water = false;
  Serial.println("Water_pump turned off");
  }   
}
// ##################################################################################  
void controlRELAYO2_on(){
if(!manual){
  digitalWrite(RELAYO2_PIN, LOW);
  status_oxygen = true;
  Serial.println("Air_pump turned on");
  }
}
// ##################################################################################
void controlRELAYO2_off(){
if(!manual){
  digitalWrite(RELAYO2_PIN, HIGH);
  status_oxygen = false;
  Serial.println("Air_pump turned off");
  }   
}
// #################################################################################
void Light(){
// TIMER Light
if (!manual) {
  if (should_light_be_on && !status_light) {
    digitalWrite(RELAYLight_PIN, LOW); 
    status_light = true;
    Serial.println("lighting on");
  } else if (!should_light_be_on && status_light) {
    digitalWrite(RELAYLight_PIN, HIGH); 
    status_light = false;
    Serial.println("lighting off");
  }
}
} 
// #################################################################################
void Watering(){
// TIMER Watering  
  if ((long)(unixTime - lastCheckWateron) >= pump_cycle && automatic) {
    controlRELAYWater_on();
    lastCheckWateron = unixTime;
    lastCheckO2on = unixTime;
    lastCheckWateroff = unixTime;
    Serial.println("Watering started");
  }
  if ((long)(unixTime - lastCheckWateroff) >= pump_duration && status_water && status_oxygen && automatic) {
    controlRELAYWater_off();
    controlRELAYO2_off();
    Serial.println("Watering ended");
  }
// TIMER Oxygen
  if ((long)(unixTime - lastCheckO2on) >= (pump_cycle - 600) && !status_oxygen && automatic) { // 10 min early
     controlRELAYO2_on();
  }
}
// #################################################################################
void Humi(){
if (!manual) {
  if (!status_humiditer && ((long)(unixTime - lastCheckHumon) >= humiinterval)) { 
    digitalWrite(RELAYHUM_PIN, LOW); 
    status_humiditer = true;
    lastCheckHumon = unixTime; 
    lastCheckHumoff = unixTime;
    Serial.println("humidification started");
  }
  
  if (status_humiditer && ((long)(unixTime - lastCheckHumoff) >= 300)) { // 5 min
    digitalWrite(RELAYHUM_PIN, HIGH); 
    status_humiditer = false; 
    Serial.println("humidification ended");
  }
}
}
// #################################################################################
/*
  Since Automatic is READ_WRITE variable, onAutomaticChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onAutomaticChange()  {
  if(automatic == true){
  timeClient.update(); 
  unixTime = timeClient.getEpochTime();
  setTime(unixTime);
  lastCheckWateron = unixTime;
  lastCheckO2on = unixTime;
  lastCheckWateroff = unixTime;
  }
}
/*
  Since AirPump is READ_WRITE variable, onAirPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onAirPumpChange()  {
  if(air_pump == true){
  manual = true;
  digitalWrite(RELAYO2_PIN, LOW);
  status_oxygen = true;
 
  } else if (air_pump == false) {
  manual = false;
  digitalWrite(RELAYO2_PIN, HIGH); 
  status_oxygen = false;  
  }
}
/*
  Since Light is READ_WRITE variable, onLightChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onLightChange()  {
  if(light == true){
  manual = true;
  digitalWrite(RELAYLight_PIN, LOW);
  status_light = true;
  Serial.println("Light on");
 
  } else if (light == false) {
  manual = false;
  digitalWrite(RELAYLight_PIN, HIGH); 
  status_light = false;  
  Serial.println("Light off");
  }
}
/*
  Since WaterPump is READ_WRITE variable, onWaterPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onWaterPumpChange()  {
   if(water_pump == true){
  manual = true;
  digitalWrite(RELAYWater_PIN, LOW);
  status_water = true;
 
  } else if (water_pump == false) {
  manual = false;
  digitalWrite(RELAYWater_PIN, HIGH); 
  status_water = false;  
  }
}
/*
  Since APump is READ_WRITE variable, onAPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onAPumpChange()  {
  if(a_pump == true){
  manual = true;
    digitalWrite(RELAYA_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump A manual on");
  } else if (a_pump == false) {
  manual = false;
  digitalWrite(RELAYA_PIN, HIGH); 
  Serial.println("Pump A manual off");
  }
}
/*
  Since BPump is READ_WRITE variable, onBPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onBPumpChange()  {
  if(b_pump == true){
  manual = true;
  digitalWrite(RELAYB_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump B manual on");
  } else if (b_pump == false) {
  manual = false;
  digitalWrite(RELAYB_PIN, HIGH);
  Serial.println("Pump B manual off");
  }
}
/*
  Since CPump is READ_WRITE variable, onCPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onCPumpChange()  {
  if(c_pump == true){
  manual = true;
  digitalWrite(RELAYC_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump C manual on"); 
  } else if (c_pump == false) {
  manual = false;
  digitalWrite(RELAYC_PIN, HIGH);
  Serial.println("Pump C manual off");
  }
}
/*
  Since PHMinusPump is READ_WRITE variable, onPHMinusPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPHMinusPumpChange()  {
  if(pH_minus_pump == true){
  manual = true;
  digitalWrite(RELAYpHm_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump pH- manual on");
  } else if (pH_minus_pump == false) {
  manual = false;
  status_ph_minus = false; 
  digitalWrite(RELAYpHm_PIN, HIGH);
  Serial.println("Pump pH- manual off");
  }
}
/*
  Since PHPlusPump is READ_WRITE variable, onPHPlusPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPHPlusPumpChange()  {
  if(pH_plus_pump == true){
  manual = true;
  digitalWrite(RELAYpHp_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump pH+ manual on");
  } else if (pH_plus_pump == false){
  manual = false;
  status_ph_plus = false;  
  digitalWrite(RELAYpHp_PIN, HIGH);
  Serial.println("Pump pH+ manual off");
  }
}
/*
  Since TimePumpCycle is READ_WRITE variable, onTimePumpCycleChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onTimePumpCycleChange()  {
  // Add your code here to act upon TimePumpCycle change
}
/*
  Since PHSetpoint is READ_WRITE variable, onPHSetpointChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPHSetpointChange()  {
  // Add your code here to act upon PHSetpoint change
}
/*
  Since PHThreshold is READ_WRITE variable, onPHThresholdChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPHThresholdChange()  {
  // Add your code here to act upon PHThreshold change
}
/*
  Since OFFTimeLight is READ_WRITE variable, onOFFTimeLightChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onEcSetpointChange()  {
  // Add your code here to act upon EcSetpoint change
}
/*
  Since EcThreshold is READ_WRITE variable, onEcThresholdChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onEcThresholdChange()  {
  // Add your code here to act upon EcThreshold change
}
/*
  Since PhMinusVol is READ_WRITE variable, onPhMinusVolChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPhMinusVolChange()  {
  // Add your code here to act upon PhMinusVol change
}
/*
  Since PhPlusVol is READ_WRITE variable, onPhPlusVolChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPhPlusVolChange()  {
  // Add your code here to act upon PhPlusVol change
}
/*
  Since Humiditer is READ_WRITE variable, onHumiditerChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onHumiditerChange()  {
  if(humiditer == true){
  digitalWrite(RELAYHUM_PIN, LOW);
  status_humiditer = true;
  } else if (humiditer == false) {
  digitalWrite(RELAYHUM_PIN, HIGH); 
  status_humiditer = false; 
  }
}
/*
  Since AbcMix is READ_WRITE variable, onAbcMixChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onAbcMixChange()  {
  // Add your code here to act upon AbcMix change
}
/*
  Since PhAdd is READ_WRITE variable, onPhAddChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPhAddChange()  {
  // Add your code here to act upon PhAdd change
}
/*
  Since PhOffset is READ_WRITE variable, onPhOffsetChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPhOffsetChange()  {
  // Add your code here to act upon PhOffset change
}
/*
  Since EcOffset is READ_WRITE variable, onEcOffsetChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onEcOffsetChange()  {
  // Add your code here to act upon EcOffset change
}
/*
   Since LightTime is READ_WRITE variable, onLightTimeChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onLightTimeChange()  {
  // Add your code here to act upon LightTime change
}
/*
  Since Reset is READ_WRITE variable, onResetChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onResetChange()  {
status_dosinglevel = false;
ph_minus_est_vol = 0;
ph_plus_est_vol = 0;  
level_A = 0;
level_B = 0;
level_C = 0;
}
/*
  Since EcFill is READ_WRITE variable, onEcFillChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onEcFillChange()  {
  if (!status_ph_plus && !status_ph_minus){
  if (ec_fill == true) {
      dose_A = vol_A * 357.0; // 1 mL; 2,80 mL/s  
      dose_B = vol_B * 382.0; // 1 mL; 2,62 mL/s
      dose_C = vol_C * 379.0; // 1 mL; 2,64 mL/s

    if (!relayAState && !relayBState && !relayCState) { 
       start_RelayA = millis();
       relayAState = true; 
       digitalWrite(RELAYA_PIN, LOW); 
       count_A++;

       start_RelayB = millis();
       relayBState = true; 
       digitalWrite(RELAYB_PIN, LOW); 
       count_B++;
   
       start_RelayC = millis();
       relayCState = true; 
       digitalWrite(RELAYC_PIN, LOW); 
       count_C++;
    }
       Serial.println("Pumps A/B/C on");
     
    if (!status_mixing_pump){
       start_MixingPumpCheck = millis();
       status_mixing_pump = true; 
       digitalWrite(RELAYMix_PIN, LOW);
       Serial.println("Mixing_pump turned on");
       } 
       Serial.println("ec low");
  } } 
}

Ich würde mich sehr freuen wenn sich jemand der Sache annehmen und mir Tipps zu Behebung des Fehlers geben würde. Dankeschön im vorraus!

Kannst du bitte deinen Sketch vervollständigen, so dass wir sehen, was an welcher stelle passieren soll.
Ohne Kommentare ist das für dritte schwer nachzuvollziehen, was da genau gemacht werden soll.

Alles klar mache ich :slight_smile:

Dann frag' mal ChatGPT warum der Schmarren nicht funktioniert.

Alternative: Schnapp' dir ein Anfängerbuch oder Tutorial und lerne Programmieren.

2 Likes

Wann verbindet sich dein Router neu mit dem Internet?

#include <DFRobot_EC.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "DHT.h"
#include <DFRobot_PH.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <TimeLib.h>
#include "thingProperties.h"
 /* 
  Sketch generated by the Arduino IoT Cloud Thing "Untitled"
  https://create.arduino.cc/cloud/things/f7f1eb64-f202-4b5a-8881-c2c96c09ce10 

  Arduino IoT Cloud Variables description

  The following variables are automatically generated and updated when changes are made to the Thing

  float ec_act;
  float ec_offset;
  float flowRate;
  float humi;
  float next_Cycle;
  float ph_act;
  float ph_minus_est_vol;
  float ph_offset;
  float ph_plus_est_vol;
  float pH_setpoint;
  float pH_threshold;
  float tempC;
  float time_pump_cycle;
  float volume;
  float wtempC;
  int abc_mix;
  int light_time;
  bool air_pump;
  bool a_pump;
  bool automatic;
  bool b_pump;
  bool c_pump;
  bool ec_fill;
  bool humiditer;
  bool light;
  bool pH_minus_pump;
  bool pH_plus_pump;
  bool reset;
  bool status_dosinglevel;
  bool status_humiditer;
  bool status_light;
  bool status_mixing_pump;
  bool status_oxygen;
  bool status_ph_minus;
  bool status_ph_plus;
  bool status_water;
  bool water_pump;

  Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
  which are called when their values are changed from the Dashboard.
  These functions are generated with the Thing and added at the end of this sketch.
*/
// ####################################################################
// Define pins
#define PH_PIN A0
#define EC_PIN A1
#define DHT22_PIN A2
#define ONE_WIRE_BUS 5
//Flowsensor_PIN 2

#define RELAYLight_PIN 4 
#define RELAYHUM_PIN 3
#define RELAYWater_PIN 6 
#define RELAYO2_PIN 7
#define RELAYMix_PIN 8

#define RELAYA_PIN 9
#define RELAYB_PIN 10
#define RELAYC_PIN 11
#define RELAYpHm_PIN 12
#define RELAYpHp_PIN 1
// ####################################################################
//Module
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DHT dht22(DHT22_PIN, DHT22);
DFRobot_PH ph;
DFRobot_EC ec;
LiquidCrystal_I2C lcd(0x27, 20, 4);
// ####################################################################
// Multiplexer
const uint8_t TCA9548A_ADDR = 0x70; 
const uint8_t LCD_CHANNEL = 0;
// ####################################################################
// pH
float ph_voltage, ph_value, ph_temp, sum_ph, doseTime_pH_plus, doseTime_pH_minus;
float phReadings[5];
int count_ph_minus = 0;
int count_ph_plus = 0;
unsigned long start_pH_minus = 0, start_pH_plus = 0; 
// ####################################################################
// EC
float ec_voltage, ec_value, ec_temp, sum_ec;
unsigned long dose_A, dose_B, dose_C;
float ecReadings[5];
float vol_A, vol_B, vol_C, level_A, level_B, level_C;
int count_A = 0;
int count_B = 0;
int count_C = 0;
unsigned long start_RelayA = 0, start_RelayB = 0, start_RelayC = 0;
bool relayAState = true, relayBState = true, relayCState = true;
// ####################################################################
//Mixing_pump
unsigned long start_MixingPumpCheck = 0; 
const unsigned long mixingPumpInterval = 180000;
// ####################################################################
//Control
unsigned long CheckControl = 0;
const unsigned long ControlInterval = 60000;
// ####################################################################
//flowrate
volatile int pulseCount;  
const int sensorPin = 2;
// ####################################################################
//NTPC
WiFiUDP ntpUDP; 
NTPClient timeClient(ntpUDP, "pool.ntp.org", 3600, 60000);  // UTC+1
unsigned long unixTime, lastCheckWateron, lastCheckWateroff, lastCheckO2on, lastCheckHumoff, lastCheckHumon;
// ####################################################################
//Light
bool should_light_be_on;
int light_on_hours;
int light_on_minutes;
int light_off_hours;
int light_off_minutes;
// ####################################################################
//pump
float pump_cycle;
const int pump_duration = 900; // 15 min
bool manual;
// ####################################################################
//Humi
const unsigned long humiinterval= 14400; // 2 h
// ####################################################################
// Calibration
static char inputBuffer[50];
int seconds = 0;  
unsigned long startTime = millis();
// ####################################################################
//Sub
void Sensor_read();
void Adjust();
void level();
void Relay_stop();
void Light();
void Watering();
void Humi();
void controlRelaymix_on();
void controlRELAYWater_on();
void controlRELAYWater_off();
void controlRELAYO2_on();
void controlRELAYO2_off();
void cloudinput();
void pulse();
void cali_menu();
void dead_cal();
void tca9548a_select(uint8_t channel);
// ####################################################################
void setup(){
  //Serial&Sensors  Initialisierung aller Sensoren für pH/EC/Flow/DHT22/Temperatur
  Serial.begin(9600);
  sensors.begin();
  dht22.begin();
  Wire.begin();
  ph.begin();
  ec.begin();
  
  //Flowrate
  pulseCount = 0; //Durchfluss-Messer
  volume = 0;  
  attachInterrupt(digitalPinToInterrupt(sensorPin), pulse, RISING);  // DIGITAL Pin 2: Interrupt 0

  // Initializing LCD
  tca9548a_select(LCD_CHANNEL); //LCD-Display ist an einem Multiplexer angeschlossen
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing...");
  lcd.setCursor(0, 2);
  lcd.print("Hydrobox-System");
  
  //Relais 
  pinMode(RELAYLight_PIN, OUTPUT); //Insgesamt sind 10 Relais verbaut die entweder Pumpen, Licht schalten
  pinMode(RELAYWater_PIN, OUTPUT);
  pinMode(RELAYO2_PIN, OUTPUT);
  pinMode(RELAYMix_PIN, OUTPUT);
  pinMode(RELAYHUM_PIN, OUTPUT);
  
  pinMode(RELAYpHm_PIN, OUTPUT);
  pinMode(RELAYpHp_PIN, OUTPUT);
  pinMode(RELAYA_PIN, OUTPUT);
  pinMode(RELAYB_PIN, OUTPUT);
  pinMode(RELAYC_PIN, OUTPUT);
  
  digitalWrite(RELAYLight_PIN, HIGH);
  digitalWrite(RELAYWater_PIN, HIGH);
  digitalWrite(RELAYO2_PIN, HIGH);
  digitalWrite(RELAYMix_PIN, HIGH);
  digitalWrite(RELAYHUM_PIN, HIGH);
  
  digitalWrite(RELAYpHm_PIN, HIGH);
  digitalWrite(RELAYpHp_PIN, HIGH);
  digitalWrite(RELAYA_PIN, HIGH);
  digitalWrite(RELAYB_PIN, HIGH);
  digitalWrite(RELAYC_PIN, HIGH);

  Serial.println("Initializing  Relais");
  tca9548a_select(LCD_CHANNEL);
  delay(1000); 
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing Relais");
  delay(1000);

  //Status reset 
  automatic = false;	// habe hier erstmal alle bools auf falsch gesetzt damit nicht sofort alle Pumpen aktiviert werden 
  manual = false;
  status_dosinglevel = false;
  status_light = false;
  status_mixing_pump = false;
  status_oxygen = false;
  status_ph_minus = false;
  status_ph_plus = false;
  status_water = false;
  status_humiditer = false;  
  relayAState = false;  
  relayBState = false; 
  relayCState = false;
  ec_fill = false;
  pump_cycle = 3600; // Das hier ist Spezifisch für die Ansteuerung der Bewässerungspumpe, da der pump_cycle erst nach einer Minute im inputcloud() geladen wird, kam es sonst dazu das die Bewässerung nach start sofort an war
  
  Serial.println("Initializing  Status");
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing Status");
  delay(1000);

  //Connect to Arduino IoT Cloud
  initProperties();
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and   errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
 */
  Serial.println("Connecting to Arduino IoT Cloud...");
  unsigned long cloudStartTime = millis();
  while (!ArduinoCloud.connected()) {
    ArduinoCloud.update();
    delay(500);
    if (millis() - cloudStartTime > 30000) { // Timeout nach 30 Sekunden
      Serial.println("Cloud connection timeout.");
      break;
    }
  }

  if (ArduinoCloud.connected()) {
    Serial.println("Connected to Arduino IoT Cloud!");
  } else {
    Serial.println("Failed to connect to IoT Cloud.");
  }
   
  tca9548a_select(LCD_CHANNEL);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing Cloud");
  delay(5000);
  
  //NTPC
  if (WiFi.status() == WL_CONNECTED) { // ich habe vorher eine RTC verwendet, diese war jedoch defekt und hat immer wieder die Uhrzeit verloren, habe online eine NTPC als Alternative gefunden, diese wird hier initialisiert
    timeClient.begin();
    timeClient.update();
    unsigned long unixTime = timeClient.getEpochTime();
    setTime(unixTime);
    Serial.print("Current time: ");
    Serial.println(unixTime);
  } else {
    Serial.println("WiFi not connected, skipping NTP initialization.");
  }

  lastCheckWateron = unixTime; //Für die Zeitsteuerung
  lastCheckO2on = unixTime;
  lastCheckWateroff = unixTime;
  lastCheckHumoff = unixTime;
  lastCheckHumon = unixTime;
  
  tca9548a_select(LCD_CHANNEL);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing NTPC");
  delay(1000);

  Serial.println("Initializing  finished"); 
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Initializing End");
  delay(1000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Loading Sensordata");
  Serial.println("Loading Sensordata");
}
// ####################################################################
void loop() {
//Update
  ArduinoCloud.update(); 
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  
//Control
  if ((unsigned long)(millis() - CheckControl) >= ControlInterval) { //Hauptfunktion die jede Minute die Sensor-Daten ausließt und entsprechend Korrekturmittel für einen optimalen pH-Wert hinzugibt, gleichzeitig wird berechnet wie viel Korrekturmittel noch vorhanden ist, Inputs aus der Cloud gelesen, die NTPC geupdatet (unixtime) und die Pulszahl für den Durchflussmesser zurückgesetzt.
    Sensor_read();
    Adjust();
    level();
    cloudinput();
    
    timeClient.update(); 
    unixTime = timeClient.getEpochTime();
    setTime(unixTime);
    
    Serial.print("Current Time: ");
    Serial.print(hour());
    Serial.print(":");
    if (minute() < 10) {
      Serial.print("0");
    }
    Serial.print(minute());
    Serial.print(":");
    if (second() < 10) {
      Serial.print("0");
    }
    Serial.println(second());
   
    pulseCount = 0;
    CheckControl = millis();
  }
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//TIMER
Light(); 	//Steuert Licht, Bewässerung und Befeuchtung nach Zeit aus der NTPC
Watering();
Humi();
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//Relay stop
  if (status_ph_minus || status_ph_plus || relayAState || relayBState || relayCState || status_mixing_pump){ //Stoppt die jeweiligen Relais nach Aktivierung über millis()
     Relay_stop(); 
  } 
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 if (Serial.available() > 0) {
        size_t len = Serial.readBytesUntil('\n', inputBuffer, sizeof(inputBuffer) - 1); 
        inputBuffer[len] = '\0'; 

        if (strcasecmp(inputBuffer, "cal") == 0) { //ruft ein Menü zur Kalibrierung der Pumpen auf
            cali_menu();
        } else if (strcasecmp(inputBuffer, "dead") == 0) { //aktiviert die Pumpen um das Totvolumen der Silikonschläuche aufzufüllen
            dead_cal();
        } else { 
            Serial.println("Incorrect input");
        }
    }  
delay(50);
}
// ##################################################################################
void tca9548a_select(uint8_t channel) {		//Für die Ansteuerung des LCD-Displays über den Multiplexer
    if (channel > 7) return;
    Wire.beginTransmission(TCA9548A_ADDR);
    Wire.write(1 << channel);
    Wire.endTransmission();
}
// ##################################################################################
void pulse(){
  pulseCount++;			//Pluszähler für den Durchflussmesser
}
// ##################################################################################
void dead_cal() { // Ausgleichen des Totvolumens
    //pH -
    doseTime_pH_minus = 6.0 * 352.4; // 6.0 mL
    start_pH_minus = millis();       
    status_ph_minus = true;   
    digitalWrite(RELAYpHm_PIN, LOW);
    //pH+
    doseTime_pH_plus = 6.6 * 478.5; // 6.6 mL
    start_pH_plus = millis();        
    status_ph_plus = true;           
    digitalWrite(RELAYpHp_PIN, LOW);
    //A
    dose_A = 6.9 * 357; // 6.9 mL
    start_RelayA = millis();
    relayAState = true; 
    digitalWrite(RELAYA_PIN, LOW); 
    //B
    dose_B = 6.4 * 382; // 6.4 mL
    start_RelayB = millis();
    relayBState = true; 
    digitalWrite(RELAYB_PIN, LOW);
    //C
    dose_C = 6.3 * 379; // 6.3 mL/s 
    start_RelayC = millis();
    relayCState = true; 
    digitalWrite(RELAYC_PIN, LOW); 
}
// ##################################################################################
void cali_menu() {
    Serial.println("Calibration started. Type 'exit' to exit or 'help' for further commands."); //Kalibrierung der Pumpen, messe manuell mit einer Waage wie viel ml nach 10sec gepumpt wurden um dann die Pumprate in Excel zu berechnen
    showHelpMenu(); 
    bool running = true; 
    String input = "";   

    while (running) {
        if (Serial.available() > 0) {
            input = Serial.readStringUntil('\n');
            input.trim(); 

            if (input.equalsIgnoreCase("exit")) {
                Serial.println("Calibration completed.");
                Serial.println("Restart!");
                running = false;
                NVIC_SystemReset();
            } else if (input.equalsIgnoreCase("Pump A")) {
                handlePump(RELAYA_PIN, "Pump A");
            } else if (input.equalsIgnoreCase("Pump B")) {
                handlePump(RELAYB_PIN, "Pump B");
            } else if (input.equalsIgnoreCase("Pump C")) {
                handlePump(RELAYC_PIN, "Pump C");
            } else if (input.equalsIgnoreCase("Pump pH+")) {
                handlePump(RELAYpHp_PIN, "Pump pH+");
            } else if (input.equalsIgnoreCase("Pump pH-")) {
                handlePump(RELAYpHm_PIN, "Pump pH-");
            } else if (input.equalsIgnoreCase("help")) {
                showHelpMenu();
            } else {
                Serial.println("Unknown command. Type 'help' for available commands.");
            }

            input = ""; 
        }
    }
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void handlePump(int pin, String pumpName) {
    Serial.println(pumpName + " started");
    digitalWrite(pin, LOW);

    unsigned long startTime = millis();
    unsigned long elapsed = 0;
    while (elapsed < 10000) { // 10 Sec
        elapsed = millis() - startTime;
        if (elapsed % 1000 == 0) { 
            Serial.print("Seconds: ");
            Serial.println(elapsed / 1000);
        }
    }

    digitalWrite(pin, HIGH);
    Serial.println(pumpName + " stopped");
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void showHelpMenu() {
    Serial.println("Available commands:");
    Serial.println("Pump A");
    Serial.println("Pump B");
    Serial.println("Pump C");
    Serial.println("Pump pH+");
    Serial.println("Pump pH-");
    Serial.println("exit");
}
// ##################################################################################
void cloudinput(){    //Hier werden die Parameter fürs Licht und die Bewässerung gesteuert sowie die Dosiermenge für die Nährstoffpumpen -> Input erfolgt über Dashboard, Dropdown menü oder ähnliches 
//Light_time
  if (light_time == 1) {
    light_on_hours = 6;  // 12h
    light_on_minutes = 0;
    light_off_hours = 18;
    light_off_minutes = 0;
  }
  if (light_time == 2) {
    light_on_hours = 6; // 15h
    light_on_minutes = 0;
    light_off_hours = 21;
    light_off_minutes = 0;
  }
  if (light_time == 3){ 
    light_on_hours = 10; // 6h
    light_on_minutes = 0;
    light_off_hours = 16;
    light_off_minutes = 0; 
  }
  if (light_time == 4){ 
    light_on_hours = 8; // 8h
    light_on_minutes = 0;
    light_off_hours = 16;
    light_off_minutes = 0; 
  }
  if (light_time == 5){ 
    light_on_hours = 7; // 10h
    light_on_minutes = 0;
    light_off_hours = 17;
    light_off_minutes = 0; 
  }
should_light_be_on = 
    (hour() > light_on_hours || (hour() == light_on_hours && minute() >= light_on_minutes)) &&
    (hour() < light_off_hours || (hour() == light_off_hours && minute() <= light_off_minutes));
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//pump-cycle
  pump_cycle = time_pump_cycle * 3600;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//next Cycle
  long timeSinceLastWateron = unixTime - lastCheckWateron;			//gibt an die Cloud zurück wie lange es bis zur nächsten Bewässerung dauert
  next_Cycle = round(pump_cycle - timeSinceLastWateron) / 60;
  if (next_Cycle < 0) {
    next_Cycle = 0;  
  }  
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  
//Nutri ~20L
   if (abc_mix == 0){
    vol_A = 10; // Grow Seed Tri-part
    vol_B = 10;
    vol_C = 10;
  }
  else if (abc_mix == 1){
    vol_A = 20; // Grow young Tri-part
    vol_B = 20;
    vol_C = 20;
  }
  else if (abc_mix == 2){
    vol_A = 36; // Grow aduld Tri-part
    vol_B = 24;
    vol_C = 12;
  }
  else if (abc_mix == 3){
    vol_A = 40; // Pre Bloom Tri-part
    vol_B = 40;
    vol_C = 30;
  }
  else if (abc_mix == 4){
    vol_A = 16; // Bloom Tri-part
    vol_B = 32;
    vol_C = 48;
  }
  else if (abc_mix == 5){
    vol_A = 0; // Riping Tri-part
    vol_B = 0;
    vol_C = 100;
  }
  else if (abc_mix == 6){
    vol_A = 50; // Masterblend 1/2
    vol_B = 50;
    vol_C = 0;
  }
  else if (abc_mix == 7){
    vol_A = 100; // Masterblend 2/2
    vol_B = 100;
    vol_C = 0; 
  } else {
    vol_A = 0; // Default values if abc_mix is invalid
    vol_B = 0;
    vol_C = 0;
    }
}
// ##################################################################################  
void Sensor_read() { 									// liest alle Sensoren aus
  // DHT22                                     
  humi = dht22.readHumidity();
  tempC = dht22.readTemperature();
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  // DS18S20 
  sensors.requestTemperatures();
  wtempC = round(sensors.getTempCByIndex(0) * 100) / 100.0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  // pH	
  sum_ph = 0;									// für 5 Messungen hintereinander aus, um einen Mittelwert auszugeben -> um eventuelle Ausreißer zu minimieren
  for (int i = 0; i < 5; i++) {
  ph_voltage = analogRead(PH_PIN) / 1024.0 * 5000;
  ph_value = ph.readPH(ph_voltage, wtempC);
  ph_temp = round((ph_value + ph_offset) * 100 ) / 100.0;
  phReadings[i] = ph_temp;
  sum_ph += ph_temp;
  }
  ph_act = round((sum_ph / 5.0) * 100) / 100;					
  if (ph_act < 0) ph_act = 0;
  if (ph_act > 14) ph_act = 14;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  // EC
  sum_ec = 0;						// für 5 Messungen hintereinander aus, um einen Mittelwert auszugeben -> um eventuelle Ausreißer zu minimieren
  for (int i = 0; i < 5; i++) {
  ec_voltage = analogRead(EC_PIN) / 1024.0 * 5000;         
  ec_value = ec.readEC(ec_voltage, wtempC);  
  ec_temp = round(((ec_value + ec_offset) * 1000) * 100) / 100.0; // µs/cm 
  ecReadings[i] = ec_temp;
  sum_ec += ec_temp; 
  }
  ec_act = (long)(sum_ec / 5.0);
  if (ec_act < 0) ec_act = 0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  //Flow
    flowRate = (pulseCount / 450.0) * 60.0;
  if (flowRate > 0){
    volume += (flowRate);}
  if (flowRate == 0){
    volume = 0;}
  if (status_water == false){ 
  volume = 0;}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  // LCD Ausgabe								// Ausgabe aller Werte auf dem LCD-Display
  tca9548a_select(LCD_CHANNEL);  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("HUM:"));
  lcd.setCursor(4, 0);
  lcd.print(humi);    

  lcd.setCursor(10, 0); 
  lcd.print(F("TEMP:"));  
  lcd.setCursor(15, 0);  
  lcd.print(tempC);  

  lcd.setCursor(0, 1); 
  lcd.print(F("WTEMP:"));  
  lcd.setCursor(6, 1);  
  lcd.print(wtempC);     

  lcd.setCursor(0, 2);
  lcd.print(F("Cycle in:"));
  lcd.setCursor(10, 2);
  lcd.print(next_Cycle);
  lcd.setCursor(14, 2);
  lcd.print("min");

  lcd.setCursor(0, 3);
  lcd.print(F("pH:"));
  lcd.setCursor(3, 3);
  lcd.print(ph_act);

  lcd.setCursor(10, 3);
  lcd.print(F("EC:"));
  lcd.setCursor(13, 3);
  lcd.print(ec_act);

  Serial.println("Sensors_read");
}
// ##################################################################################  
void Adjust (){											//Steuert die Korrekturzugabe bei unter oder überschreiten eines Sollwerts und aktiviert eine Mischpumpe
// pH > Setpoint
  if (ph_act > pH_setpoint + pH_threshold && !manual) {
       doseTime_pH_minus = (1.5) * 358.4; // 1 mL; 2,79 mL/s
      
       if (!status_ph_minus) { 
          start_pH_minus = millis();       
          status_ph_minus = true;   
          digitalWrite(RELAYpHm_PIN, LOW);  
          Serial.println("Pump pH- on");
          count_ph_minus++;
         }
       if (!status_mixing_pump){
          start_MixingPumpCheck = millis();
          status_mixing_pump = true; 
          digitalWrite(RELAYMix_PIN, LOW);
          Serial.println("Mixing_pump turned on");
         } 
        Serial.println("pH high");
     } 
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// pH < Setpoint
  if (ph_act < pH_setpoint - pH_threshold && !manual) {
      doseTime_pH_plus = (1.5) * 478.5; // 1 mL; 2,09 mL/s
       
     if (!status_ph_plus) { 
        start_pH_plus = millis();        
        status_ph_plus = true;           
        digitalWrite(RELAYpHp_PIN, LOW); 
        Serial.println("Pump pH+ on");
        count_ph_plus++;
        }
     if (!status_mixing_pump){
        start_MixingPumpCheck = millis();
        status_mixing_pump = true; 
        digitalWrite(RELAYMix_PIN, LOW);
        Serial.println("Mixing_pump turned on");
        } 
        Serial.println("pH low");
    } 
  Serial.println("Adjusted");
}
// ################################################################################## 
void level() { 											//Berechnet wie viel Korrekturmittel noch vorhanden ist und gibt true Flag aus, wenn eins leer geworden ist -> in Dashboard sichtbar
  //Correction Agent level 
  ph_minus_est_vol = 100 - (count_ph_minus * (1.5));
  ph_plus_est_vol  = 100 - (count_ph_plus * (1.5));
  if (ph_minus_est_vol <= 5 || ph_plus_est_vol <= 5){
  status_dosinglevel = true;
  }
  //Nutri level
  level_A = 100 - (count_A * vol_A);
  level_B = 100 - (count_B * vol_A);
  level_C = 100 - (count_C * vol_A);
  if (level_A <= 5 || level_B <= 5 || level_C <= 5){
  status_dosinglevel = true;
  }
}
// ##################################################################################
void Relay_stop() {											//Stoppt die Relais nach Dosierzeit um ein spezifisches Volumen zu erreichen 
  //Peristaltic Relays
   if (status_ph_minus == true && ((unsigned long)(millis() - start_pH_minus) >= doseTime_pH_minus)) {
    digitalWrite(RELAYpHm_PIN, HIGH);      
    status_ph_minus = false; 
    Serial.println("Pumpe ph- aus");
  }
   if (status_ph_plus == true && ((unsigned long)(millis() - start_pH_plus) >= doseTime_pH_plus)) {
    digitalWrite(RELAYpHp_PIN, HIGH);       
    status_ph_plus = false;    
    Serial.println("Pumpe ph+ aus");
  }
    if (relayAState == true && ((unsigned long)(millis() - start_RelayA) >= dose_A )) {
    digitalWrite(RELAYA_PIN, HIGH); 
    relayAState = false; 
    Serial.println("Pumpe A aus");
  }
    if (relayBState == true && ((unsigned long)(millis() - start_RelayB) >= dose_B )) {
    digitalWrite(RELAYB_PIN, HIGH); 
    relayBState = false;  
    Serial.println("Pumpe B aus");
  }
    if (relayCState == true && ((unsigned long)(millis() - start_RelayC) >= dose_C )) {
    digitalWrite(RELAYC_PIN, HIGH); 
    relayCState = false; 
    Serial.println("Pumpe C aus");
  }
  //Mixing pump																//Stoppt die Mischpumpe nach 3min
  if ((unsigned long)(millis() - start_MixingPumpCheck) >= mixingPumpInterval && status_mixing_pump == true) {
    digitalWrite(RELAYMix_PIN, HIGH); 
    status_mixing_pump = false;
    Serial.println("Mixing_pump turned off");
  }
}
// ##################################################################################   
void controlRELAYWater_on(){									//Steuerung der Bewässerungspumpe							
 if(!manual){
  status_water = true;
  digitalWrite(RELAYWater_PIN, LOW);
  Serial.println("Water_pump turned on");
  }      
}
// ##################################################################################
void controlRELAYWater_off(){
 if(!manual){
  digitalWrite(RELAYWater_PIN, HIGH);
  status_water = false;
  Serial.println("Water_pump turned off");
  }   
}
// ##################################################################################  
void controlRELAYO2_on(){									//Steuerung der Sauerstoffpumpe
if(!manual){
  digitalWrite(RELAYO2_PIN, LOW);
  status_oxygen = true;
  Serial.println("Air_pump turned on");
  }
}
// ##################################################################################
void controlRELAYO2_off(){
if(!manual){
  digitalWrite(RELAYO2_PIN, HIGH);
  status_oxygen = false;
  Serial.println("Air_pump turned off");
  }   
}
// #################################################################################
void Light(){											//Schaltet das Licht zu einer bestimmten Uhrzeit ein und aus
// TIMER Light
if (!manual) {
  if (should_light_be_on && !status_light) {
    digitalWrite(RELAYLight_PIN, LOW); 
    status_light = true;
    Serial.println("lighting on");
  } else if (!should_light_be_on && status_light) {
    digitalWrite(RELAYLight_PIN, HIGH); 
    status_light = false;
    Serial.println("lighting off");
  }
}
} 
// #################################################################################
void Watering(){									//Schaltet die Bewässerungspumpe nach X Zeit ein, lässt sie 15 min laufen und schaltet sie dann + die Sauerstoffpumpe ab
// TIMER Watering  
  if ((long)(unixTime - lastCheckWateron) >= pump_cycle && automatic) {
    controlRELAYWater_on();
    lastCheckWateron = unixTime;
    lastCheckO2on = unixTime;
    lastCheckWateroff = unixTime;
    Serial.println("Watering started");
  }
  if ((long)(unixTime - lastCheckWateroff) >= pump_duration && status_water && status_oxygen && automatic) {
    controlRELAYWater_off();
    controlRELAYO2_off();
    Serial.println("Watering ended");
  }
// TIMER Oxygen
  if ((long)(unixTime - lastCheckO2on) >= (pump_cycle - 600) && !status_oxygen && automatic) { 		//schaltet die Sauerstoffpumpe 10 min vor der Bewässerungpumpe ein
     controlRELAYO2_on();
  }
}
// #################################################################################
void Humi(){
if (!manual) {
  if (!status_humiditer && ((long)(unixTime - lastCheckHumon) >= humiinterval)) { 			//Steuert das Ein und Abschaltet eines Luftbefeuchters alle 4h für 5 min
    digitalWrite(RELAYHUM_PIN, LOW); 
    status_humiditer = true;
    lastCheckHumon = unixTime; 
    lastCheckHumoff = unixTime;
    Serial.println("humidification started");
  }
  
  if (status_humiditer && ((long)(unixTime - lastCheckHumoff) >= 300)) { // 5 min
    digitalWrite(RELAYHUM_PIN, HIGH); 
    status_humiditer = false; 
    Serial.println("humidification ended");
  }
}
}
// #################################################################################
/*
  Since Automatic is READ_WRITE variable, onAutomaticChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onAutomaticChange()  {							//Einschaltet der automatischen Steuerung sowie zurücksetzen des Zählers -> pump_cycle
  if(automatic == true){
  timeClient.update(); 
  unixTime = timeClient.getEpochTime();
  setTime(unixTime);
  lastCheckWateron = unixTime;
  lastCheckO2on = unixTime;
  lastCheckWateroff = unixTime;
  }
}
/*
  Since AirPump is READ_WRITE variable, onAirPumpChange() is
  executed every time a new value is received from IoT Cloud.			//hier folgt jetzt die manuelle Steuerung für jede Pumpe/ Licht/ Befeuchtung
*/
void onAirPumpChange()  {
  if(air_pump == true){
  manual = true;
  digitalWrite(RELAYO2_PIN, LOW);
  status_oxygen = true;
 
  } else if (air_pump == false) {
  manual = false;
  digitalWrite(RELAYO2_PIN, HIGH); 
  status_oxygen = false;  
  }
}
/*
  Since Light is READ_WRITE variable, onLightChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onLightChange()  {
  if(light == true){
  manual = true;
  digitalWrite(RELAYLight_PIN, LOW);
  status_light = true;
  Serial.println("Light on");
 
  } else if (light == false) {
  manual = false;
  digitalWrite(RELAYLight_PIN, HIGH); 
  status_light = false;  
  Serial.println("Light off");
  }
}
/*
  Since WaterPump is READ_WRITE variable, onWaterPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onWaterPumpChange()  {
   if(water_pump == true){
  manual = true;
  digitalWrite(RELAYWater_PIN, LOW);
  status_water = true;
 
  } else if (water_pump == false) {
  manual = false;
  digitalWrite(RELAYWater_PIN, HIGH); 
  status_water = false;  
  }
}
/*
  Since APump is READ_WRITE variable, onAPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onAPumpChange()  {
  if(a_pump == true){
  manual = true;
    digitalWrite(RELAYA_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump A manual on");
  } else if (a_pump == false) {
  manual = false;
  digitalWrite(RELAYA_PIN, HIGH); 
  Serial.println("Pump A manual off");
  }
}
/*
  Since BPump is READ_WRITE variable, onBPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onBPumpChange()  {
  if(b_pump == true){
  manual = true;
  digitalWrite(RELAYB_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump B manual on");
  } else if (b_pump == false) {
  manual = false;
  digitalWrite(RELAYB_PIN, HIGH);
  Serial.println("Pump B manual off");
  }
}
/*
  Since CPump is READ_WRITE variable, onCPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onCPumpChange()  {
  if(c_pump == true){
  manual = true;
  digitalWrite(RELAYC_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump C manual on"); 
  } else if (c_pump == false) {
  manual = false;
  digitalWrite(RELAYC_PIN, HIGH);
  Serial.println("Pump C manual off");
  }
}
/*
  Since PHMinusPump is READ_WRITE variable, onPHMinusPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPHMinusPumpChange()  {
  if(pH_minus_pump == true){
  manual = true;
  digitalWrite(RELAYpHm_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump pH- manual on");
  } else if (pH_minus_pump == false) {
  manual = false;
  status_ph_minus = false; 
  digitalWrite(RELAYpHm_PIN, HIGH);
  Serial.println("Pump pH- manual off");
  }
}
/*
  Since PHPlusPump is READ_WRITE variable, onPHPlusPumpChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPHPlusPumpChange()  {
  if(pH_plus_pump == true){
  manual = true;
  digitalWrite(RELAYpHp_PIN, LOW);
  status_mixing_pump = true;
  digitalWrite(RELAYMix_PIN, LOW);
  Serial.println("Pump pH+ manual on");
  } else if (pH_plus_pump == false){
  manual = false;
  status_ph_plus = false;  
  digitalWrite(RELAYpHp_PIN, HIGH);
  Serial.println("Pump pH+ manual off");
  }
}
/*
  Since TimePumpCycle is READ_WRITE variable, onTimePumpCycleChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onTimePumpCycleChange()  {
  // Add your code here to act upon TimePumpCycle change
}
/*
  Since PHSetpoint is READ_WRITE variable, onPHSetpointChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPHSetpointChange()  {
  // Add your code here to act upon PHSetpoint change
}
/*
  Since PHThreshold is READ_WRITE variable, onPHThresholdChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPHThresholdChange()  {
  // Add your code here to act upon PHThreshold change
}
/*
  Since OFFTimeLight is READ_WRITE variable, onOFFTimeLightChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onEcSetpointChange()  {
  // Add your code here to act upon EcSetpoint change
}
/*
  Since EcThreshold is READ_WRITE variable, onEcThresholdChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onEcThresholdChange()  {
  // Add your code here to act upon EcThreshold change
}
/*
  Since PhMinusVol is READ_WRITE variable, onPhMinusVolChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPhMinusVolChange()  {
  // Add your code here to act upon PhMinusVol change
}
/*
  Since PhPlusVol is READ_WRITE variable, onPhPlusVolChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPhPlusVolChange()  {
  // Add your code here to act upon PhPlusVol change
}
/*
  Since Humiditer is READ_WRITE variable, onHumiditerChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onHumiditerChange()  {
  if(humiditer == true){
  digitalWrite(RELAYHUM_PIN, LOW);
  status_humiditer = true;
  } else if (humiditer == false) {
  digitalWrite(RELAYHUM_PIN, HIGH); 
  status_humiditer = false; 
  }
}
/*
  Since AbcMix is READ_WRITE variable, onAbcMixChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onAbcMixChange()  {
  // Add your code here to act upon AbcMix change
}
/*
  Since PhAdd is READ_WRITE variable, onPhAddChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPhAddChange()  {
  // Add your code here to act upon PhAdd change
}
/*
  Since PhOffset is READ_WRITE variable, onPhOffsetChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onPhOffsetChange()  {
  // Add your code here to act upon PhOffset change
}
/*
  Since EcOffset is READ_WRITE variable, onEcOffsetChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onEcOffsetChange()  {
  // Add your code here to act upon EcOffset change
}
/*
   Since LightTime is READ_WRITE variable, onLightTimeChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onLightTimeChange()  {
  // Add your code here to act upon LightTime change
}
/*
  Since Reset is READ_WRITE variable, onResetChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onResetChange()  {								//setzt das Korrekturlevel zurück
status_dosinglevel = false;
ph_minus_est_vol = 0;
ph_plus_est_vol = 0;  
level_A = 0;
level_B = 0;
level_C = 0;
}
/*
  Since EcFill is READ_WRITE variable, onEcFillChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onEcFillChange()  {
  if (!status_ph_plus && !status_ph_minus){					//Dosiert Nährstoffe, nach zuvor in Cloudinput definierten Mengen -> vol_A, etc.
  if (ec_fill == true) {
      dose_A = vol_A * 357.0; // 1 mL; 2,80 mL/s  
      dose_B = vol_B * 382.0; // 1 mL; 2,62 mL/s
      dose_C = vol_C * 379.0; // 1 mL; 2,64 mL/s

    if (!relayAState && !relayBState && !relayCState) { 
       start_RelayA = millis();
       relayAState = true; 
       digitalWrite(RELAYA_PIN, LOW); 
       count_A++;

       start_RelayB = millis();
       relayBState = true; 
       digitalWrite(RELAYB_PIN, LOW); 
       count_B++;
   
       start_RelayC = millis();
       relayCState = true; 
       digitalWrite(RELAYC_PIN, LOW); 
       count_C++;
    }
       Serial.println("Pumps A/B/C on");
     
    if (!status_mixing_pump){
       start_MixingPumpCheck = millis();
       status_mixing_pump = true; 
       digitalWrite(RELAYMix_PIN, LOW);
       Serial.println("Mixing_pump turned on");
       } 
       Serial.println("ec low");
  } } 
}

wow Kollege deine Antwort hättest du dir sparen können.

Wie finde ich das raus? bzw, dann wäre es ja alle 24h oder? nicht alle 48 h

bzw wird im Loop auch nicht mehr meine Funktion "Control" aufgerufen und es erfolgt eine Ausgabe im Seriellen Monitor, das müsste ja da es unabhängig von der Cloud läuft, funktionieren

Nein, hätte ich nicht: du bist in ein düsteres Hafenviertel gegangen, hast dir die schmuddligste Bar ausgesucht, hast dort den erst besten Saufkumpan nach Code gefragt, hast erwas bekommen, das kein Schneee war, bist jetzt zu Hause draufgekommen dass was falsch ist und gehst in eine andere Bar, sagst den Leuten dort dass due im düsteren Havenviertel in einer schmuddeligen Bar von einem bekifften Säufer etwas bekommen hast und .... :scream:

okay, dann hilf mir doch einfach schreib doch wenigsten was ich ausbessern müsste. Ist ja nicht so das der gesamte Code von Chat gpt ist. Die Hauptstruktur habe ich mir im Übrigen selbst nach Tutorials erstellt. Es würde mir ja reichen ein paar Stichworte zu bekommen wonach ich ausschau halten muss. Denn nicht alles steht in einem Anfänger Handbuch!

wie äußert sich das genau? Was macht der Sketch noch als letztes - was macht er nicht mehr?


hier mal ein Ausschnitt vom Dashboard. Der Code läuft bis 5:04 in diesem Fall durch und bleibt dann stehen

Das hilft wenig.
Was gibt der Uno R4 (wifi???) noch aus - auf LCD und Serial
Was nicht mehr?

oder siehst du nur im Dashboard keine Werte mehr , dann würde ich zwei Sachen im Loop ergänzen:

  • prüfen ob wifi verbunden ist - gebenenfalls wieder verbinden
  • prüfen ob diese Cloud-Anbindung da ist - gegebenfalls wieder verbinden.

ungeprüft

void checkCloud() {
  if (!ArduinoCloud.connected()) {
    Serial.println("try reconnecting cloud");
    // evtl. auch was am LCD ausgeben
    ArduinoCloud.update();
  }
}

wie man ein checkWifi am R4 macht hab ich noch keine gute Idee...

void checkWifi() { 
  if (WiFi.status() != WL_CONNECTED) {
   Serial.println("reconnected wifi necessary");
   // irgend ein magic befehl für den R4 ...
  }
}

Auf dem LCD bleibt die letzte Ausgabe erhalten. Vermute mal das der Code sich am Control aufhängt: (also das hier)

//Control
  if ((unsigned long)(millis() - CheckControl) >= ControlInterval) { //Hauptfunktion die jede Minute die Sensor-Daten ausließt und entsprechend Korrekturmittel für einen optimalen pH-Wert hinzugibt, gleichzeitig wird berechnet wie viel Korrekturmittel noch vorhanden ist, Inputs aus der Cloud gelesen, die NTPC geupdatet (unixtime) und die Pulszahl für den Durchflussmesser zurückgesetzt.
    Sensor_read();
    Adjust();
    level();
    cloudinput();

    timeClient.update(); 
    unixTime = timeClient.getEpochTime();
    setTime(unixTime);

    Serial.print("Current Time: ");
    Serial.print(hour());
    Serial.print(":");
    if (minute() < 10) {
      Serial.print("0");
    }
    Serial.print(minute());
    Serial.print(":");
    if (second() < 10) {
      Serial.print("0");
    }
    Serial.println(second());

    pulseCount = 0;
    CheckControl = millis();
  }

Das mit dem wiederverbinden von wifi hatte ich schon versucht, Problem tritt aber trotzdem auf. bzw verbindet sich dann auch nicht neu

was ist in diesem Fall die letzte Ausgabe am LCD, was auf der Serial?

das sind dann die Messwerte von 5:03 auf dem LCD

Die Verwendung der String-Klasse auf einem Board mit wenig Speicher kann zu Speicherproblemen führen, wenn sie nicht sorgfältig eingesetzt wird.

okay, ab wann ist es denn zu wenig Speicher für einen String? Wenn ich den Code so uploade kriege ich diese Ausgabe:

Sketch uses 137616 bytes (52%) of program storage space. Maximum is 262144 bytes.
Global variables use 15236 bytes (46%) of dynamic memory, leaving 17532 bytes for local variables. Maximum is 32768 bytes.

aus welcher Programmzeile?