Skip to content

Commit 54c6be4

Browse files
author
Dave
committed
Add option to set Raspberry Pi Zero 2w automatically so it can use async if it is. Updated main webpage and added route so can show the status effectively, currently polls every 1 second to get updates. Added system stats webpage including package versions
1 parent af3c19f commit 54c6be4

File tree

7 files changed

+706
-21
lines changed

7 files changed

+706
-21
lines changed

main.py

Lines changed: 173 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
import os
2626
import subprocess
2727
import math
28+
import psutil
29+
import platform
30+
import importlib.metadata
2831

2932
# --------------------------------------------------------------------------------
3033
# TUNING PARAMETERS (SET FROM my_confgiuration.py)
@@ -75,12 +78,67 @@
7578
manual_home_pan = HOME_PAN
7679
manual_home_tilt = HOME_TILT
7780

81+
# -----------------------------------------------------------------------------
82+
# Auto detection of Platform
83+
# -----------------------------------------------------------------------------
84+
def detect_platform():
85+
"""
86+
Reads /proc/device-tree/model and sets config.RASPBERRY_PI_ZERO_2W
87+
to True if it's a Raspberry Pi Zero 2 W, otherwise False.
88+
Logs the detected platform via logger.info.
89+
"""
90+
model_str = "Unknown Platform"
91+
model_path = "/proc/device-tree/model"
92+
93+
if os.path.exists(model_path):
94+
with open(model_path, "r") as f:
95+
model_str = f.read().strip()
96+
97+
logger.info(f"Detected Platform - {model_str}")
98+
99+
# Set config.RASPBERRY_PI_ZERO_2W based on substring match
100+
config.RASPBERRY_PI_ZERO_2W = ("Raspberry Pi Zero 2 W" in model_str)
101+
102+
detect_platform()
103+
if config.RASPBERRY_PI_ZERO_2W:
104+
logger.info("This is a Raspberry Pi Zero 2 W, enabling Pi Zero optimizations...")
105+
else:
106+
logger.info("Not a Pi Zero 2 W, using normal behavior.")
107+
108+
78109
# -----------------------------------------------------------------------------
79110
# Web Server settings, Functions and Routes
80111
# -----------------------------------------------------------------------------
81112
app = Flask(__name__)
82113
latest_frame = None # We'll store the latest camera frame in memory
83114

115+
def get_cpu_temperature():
116+
"""Return CPU temperature in °C if available, otherwise None."""
117+
temp_path = "/sys/class/thermal/thermal_zone0/temp"
118+
if os.path.exists(temp_path):
119+
try:
120+
with open(temp_path, "r") as f:
121+
millideg = f.read().strip()
122+
return float(millideg) / 1000.0 # convert from millideg to °C
123+
except Exception:
124+
pass
125+
return None
126+
127+
def get_platform_name():
128+
"""
129+
Return the platform name from /proc/device-tree/model if available,
130+
else a fallback to platform.platform().
131+
"""
132+
model_path = "/proc/device-tree/model"
133+
if os.path.exists(model_path):
134+
try:
135+
with open(model_path, "r") as f:
136+
return f.read().strip()
137+
except Exception:
138+
pass
139+
# Fallback for non-Raspberry Pi or missing file
140+
return platform.platform()
141+
84142
def start_web_server():
85143
# Run Flask dev server on port 5000 (or any port you want)
86144
app.run(host="0.0.0.0", port=5000, debug=False, threaded=True)
@@ -108,6 +166,116 @@ def gen_frames():
108166
# If no frame yet, just sleep briefly
109167
time.sleep(0.05)
110168

169+
@app.route("/system_info")
170+
def system_info():
171+
# 1) Temperature
172+
temperature = get_cpu_temperature()
173+
174+
# 2) CPU usage (percentage)
175+
cpu_usage = psutil.cpu_percent(interval=0.1)
176+
177+
# 3) Memory info
178+
mem = psutil.virtual_memory()
179+
mem_total_mb = mem.total / (1024 * 1024)
180+
mem_used_mb = mem.used / (1024 * 1024)
181+
mem_free_mb = mem.available / (1024 * 1024)
182+
183+
# 4) Swap info
184+
swap = psutil.swap_memory()
185+
swap_total_mb = swap.total / (1024 * 1024)
186+
swap_used_mb = swap.used / (1024 * 1024)
187+
188+
# 5) Root filesystem disk usage
189+
disk = psutil.disk_usage('/')
190+
disk_total_mb = disk.total / (1024 * 1024)
191+
disk_used_mb = disk.used / (1024 * 1024)
192+
disk_free_mb = disk.free / (1024 * 1024)
193+
194+
# 6) Platform name (Pi model or fallback)
195+
platform_name = get_platform_name()
196+
197+
# 7) OS version (e.g. "Linux-6.1.21-v7+-armv7l-with-glibc2.31")
198+
os_version = platform.platform()
199+
200+
# 8) Python version (e.g. "3.9.2")
201+
python_version = platform.python_version()
202+
203+
data = {
204+
"temperature": temperature, # float or None
205+
"cpu_usage": cpu_usage, # float
206+
"memory": {
207+
"total_mb": round(mem_total_mb, 2),
208+
"used_mb": round(mem_used_mb, 2),
209+
"free_mb": round(mem_free_mb, 2),
210+
},
211+
"swap": {
212+
"total_mb": round(swap_total_mb, 2),
213+
"used_mb": round(swap_used_mb, 2),
214+
},
215+
"disk": {
216+
"total_mb": round(disk_total_mb, 2),
217+
"used_mb": round(disk_used_mb, 2),
218+
"free_mb": round(disk_free_mb, 2),
219+
},
220+
"platform_name": platform_name,
221+
"os_version": os_version,
222+
"python_version": python_version,
223+
}
224+
225+
return jsonify(data)
226+
227+
228+
@app.route("/packages")
229+
def list_packages():
230+
"""
231+
Returns a JSON mapping of {package_name: version} for all installed packages
232+
in the current Python environment, using importlib.metadata.
233+
"""
234+
# Retrieve all distributions
235+
distributions = importlib.metadata.distributions()
236+
237+
installed = {}
238+
for dist in distributions:
239+
# dist.metadata is an email.message.Message object
240+
# dist.metadata["Name"] and dist.metadata["Version"] are standard fields
241+
name = dist.metadata["Name"]
242+
version = dist.metadata["Version"]
243+
installed[name] = version
244+
245+
# Sort by package name (case-insensitive) for cleaner output
246+
sorted_installed = dict(sorted(installed.items(), key=lambda x: x[0].lower()))
247+
248+
return jsonify(sorted_installed)
249+
250+
@app.route("/system")
251+
def system_page():
252+
"""
253+
Renders the 'system.html' template which shows CPU/Memory/Disk stats,
254+
Raspberry Pi info, etc.
255+
"""
256+
return render_template("system.html")
257+
258+
@app.route("/status")
259+
def status():
260+
"""
261+
Returns a JSON response containing the current status of the system:
262+
- auto_mode: boolean (True for auto, False for manual)
263+
- is_recording: boolean (current recording state)
264+
- water_pistol_active: boolean (whether the water pistol is firing)
265+
- current_pan_angle: float
266+
- current_tilt_angle: float
267+
"""
268+
current_pan, current_tilt = pan_tilt_control.get_current_angles()
269+
270+
data = {
271+
"auto_mode": auto_mode, # True/False
272+
"is_recording": recording_manager.recording, # True/False
273+
"water_pistol_active": water_pistol.active, # True/False
274+
"current_pan_angle": current_pan, # numeric value
275+
"current_tilt_angle": current_tilt, # numeric value
276+
}
277+
return jsonify(data)
278+
111279

112280
@app.route("/recordings")
113281
def show_recordings():
@@ -528,9 +696,11 @@ def stop_recording(self):
528696
self.recording = False
529697

530698
#Now convert the file
531-
convert_saved_video(self.filename)
532-
# USe the below instead if on Raspberry Pi Zero2w
533-
# convert_saved_video_async(self.filename)
699+
if config.RASPBERRY_PI_ZERO_2W == False:
700+
convert_saved_video(self.filename)
701+
else:
702+
convert_saved_video_async(self.filename)
703+
534704

535705

536706
class TargetTracker:

my_configuration.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Setup all the configurable aspects in this file
22

3+
RASPBERRY_PI_ZERO_2W = False # This will be auto updated by code now as will detect the platform.
4+
35
#Setup the logging
46
LOG_LEVEL = "DEBUG" # or "DEBUG", "WARNING", "ERROR", "CRITICAL"
57
LOG_FILE = None # If set to a filename (e.g. "my_log.log"), logs to a file; if None, logs to console

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
RPi.GPIO
22
Adafruit-PCA9685
3-
Flask
3+
Flask
4+
psutil

0 commit comments

Comments
 (0)