Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Scripts/Common.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
CONFIG_UPDATE_AVAILABLE = True
NO_CONFIG_UPDATE = False

DB_CHECK_INTERVAL = 5 # Check rtDb every 5 seconds for periodic updates

# ------[_]-------- <------------|
# | | 25 |
# |...............| <------ |
Expand All @@ -47,7 +49,7 @@
MAX_WATER_LEVEL = TANK_HEIGHT - TOP_EMPTY_DISTANCE
TWO_THIRD_LEVEL = MAX_WATER_LEVEL // 3 * 2 # Two-thirds full level

CHECK_INTERVAL_SECONDS = 1
CHECK_INTERVAL_SECONDS = 5 # Check distance every 5 seconds
MAX_PRE_NIGHT_FILL_TIME = 600

NIGHT_12AM = 0
Expand Down
127 changes: 88 additions & 39 deletions Scripts/Main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from GpioManager import GpioManager
import time
import os
from datetime import datetime

from PinDescription import *
Expand Down Expand Up @@ -64,6 +65,7 @@ def isNightTime():
def main():
gpio = GpioManager()
setupGpio(gpio)
lastDistance = readDistance(gpio, ULTRASONIC_TRIG, ULTRASONIC_ECHO)

# Default valves and durations
valve1Duration = VALVE1_DEFAULT_DURATION
Expand All @@ -83,10 +85,19 @@ def main():

try:
lastCheckTime = time.time()
lastRtDbCheck = time.time()
preNightFillActive = False
preNightFillStartTime = None

# File modification tracking for efficient change detection
lastRtDbModTime = 0
rtDb = readRtDb() # Initial read
try:
lastRtDbModTime = os.path.getmtime(RT_DB_FILE)
except OSError:
pass

while True:
print("Main loop iteration...")
currentTime = time.time()
now = datetime.now()

Expand All @@ -97,47 +108,73 @@ def main():
aftrn5RunDone = False
lastDay = now.day

rtDb = readRtDb()
configUpdateAvailable = rtDb.get("configUpdateAvailable", False)
# Efficient database change detection using file modification time
configUpdateAvailable = False
needsDbRead = False

# Check if file has been modified (lightweight operation)
try:
currentModTime = os.path.getmtime(RT_DB_FILE)
if currentModTime != lastRtDbModTime:
print("Database Updated - Reading immediately")
needsDbRead = True
lastRtDbModTime = currentModTime
elif currentTime - lastRtDbCheck >= DB_CHECK_INTERVAL:
# Periodic check even if file hasn't changed (for safety)
needsDbRead = True
except OSError:
# File doesn't exist or can't be accessed, try periodic read
if currentTime - lastRtDbCheck >= DB_CHECK_INTERVAL:
needsDbRead = True

if needsDbRead:
rtDb = readRtDb()
configUpdateAvailable = rtDb.get("configUpdateAvailable", False)
lastRtDbCheck = currentTime
if configUpdateAvailable:
print("Processing config update from web interface")
# If no read needed, use cached rtDb (already initialized above)

# Check for config updates for valves
if configUpdateAvailable:
valve1Duration = rtDb.get("valve1Duration", VALVE1_DEFAULT_DURATION)
valve2Duration = rtDb.get("valve2Duration", VALVE2_DEFAULT_DURATION)
print(f"Updated valve durations: Valve1={valve1Duration} min, Valve2={valve2Duration} min")

# Morning valve operation
if now.hour == MORNING_8AM and not morning8RunDone:
print("Morning run: Activating valves.")
gpio.output(VALVE1_PIN, True)
gpio.output(VALVE2_PIN, True)
valve1On = True
valve2On = True
valve1StartTime = currentTime
valve2StartTime = currentTime
morning8RunDone = True

# Afternoon valve operation
if now.hour == AFTERNOON_5PM and not aftrn5RunDone:
print("Afternoon run: Activating valves.")
gpio.output(VALVE1_PIN, True)
gpio.output(VALVE2_PIN, True)
valve1On = True
valve2On = True
valve1StartTime = currentTime
valve2StartTime = currentTime
aftrn5RunDone = True

# Evening valve operation
if now.hour == NIGHT_9PM and not evening9RunDone:
print("Evening run: Activating valves.")
gpio.output(VALVE1_PIN, True)
gpio.output(VALVE2_PIN, True)
valve1On = True
valve2On = True
valve1StartTime = currentTime
valve2StartTime = currentTime
evening9RunDone = True
print(f"Updated valve durations: Valve1 = {valve1Duration} min, Valve2 = {valve2Duration} min")

# Only check for scheduled valve operations once per minute to reduce processing
if now.second == 0: # Only check at the beginning of each minute
Comment on lines +144 to +145
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition may cause valve operations to be missed if the loop iteration doesn't align with second == 0. Consider using a minute-based timestamp comparison instead of relying on the exact second alignment.

Copilot uses AI. Check for mistakes.
# Morning valve operation
if now.hour == MORNING_8AM and not morning8RunDone:
print("Morning run: Activating valves.")
gpio.output(VALVE1_PIN, True)
gpio.output(VALVE2_PIN, True)
valve1On = True
valve2On = True
valve1StartTime = currentTime
valve2StartTime = currentTime
morning8RunDone = True

# Afternoon valve operation
if now.hour == AFTERNOON_5PM and not aftrn5RunDone:
print("Afternoon run: Activating valves.")
gpio.output(VALVE1_PIN, True)
gpio.output(VALVE2_PIN, True)
valve1On = True
valve2On = True
valve1StartTime = currentTime
valve2StartTime = currentTime
aftrn5RunDone = True

# Evening valve operation
if now.hour == NIGHT_9PM and not evening9RunDone:
print("Evening run: Activating valves.")
gpio.output(VALVE1_PIN, True)
gpio.output(VALVE2_PIN, True)
valve1On = True
valve2On = True
valve1StartTime = currentTime
valve2StartTime = currentTime
evening9RunDone = True

# Check to turn off valves
if valve1On and (currentTime - valve1StartTime >= valve1Duration * 60):
Expand All @@ -153,7 +190,13 @@ def main():
# Original motor logic
if currentTime - lastCheckTime >= CHECK_INTERVAL_SECONDS:
distance = readDistance(gpio, ULTRASONIC_TRIG, ULTRASONIC_ECHO)
print(f"Distance: {distance} cm")
if distanceIsValid(distance, lastDistance):
print(f"Distance: {distance} cm")
lastDistance = distance
else:
print(f"Invalid distance reading: {distance} cm; SKIP;")
time.sleep(0.5)
continue
waterLevel = TANK_HEIGHT - distance
motorStatus = rtDb.get("motorStatus", "OFF")

Expand Down Expand Up @@ -206,7 +249,13 @@ def main():
lastMotorStatus = motorStatus
lastWaterLevel = waterLevel

time.sleep(0.1) # Sleep to reduce CPU usage
# Smart sleep: shorter sleep when config updates are expected
if configUpdateAvailable:
# If we just processed a config update, check again sooner
time.sleep(0.5) # Quick follow-up check
else:
# Normal operation - longer sleep for CPU efficiency
time.sleep(1)
Comment on lines +252 to +258
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic numbers 0.5 and 1 for sleep intervals should be defined as named constants in Common.py for better maintainability and consistency with other timing constants.

Copilot uses AI. Check for mistakes.
except KeyboardInterrupt:
cleanupGpio(gpio)

Expand Down
10 changes: 10 additions & 0 deletions Scripts/Utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,13 @@ def ifWaterLevelBelowMin(waterLevel):
return False
else:
return False


def distanceIsValid(distance, lastDistance):
if distance <= 0:
return False
if lastDistance > 0:
change = abs(distance - lastDistance)
if change / lastDistance > 0.1: # More than 10% change
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic number 0.1 (10% threshold) should be defined as a named constant to make it configurable and improve code readability.

Copilot uses AI. Check for mistakes.
return False
return True
2 changes: 1 addition & 1 deletion config.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
appVersion: 1.2.2.1005
appVersion: 1.2.3.1006