add button event and screeens

This commit is contained in:
Your Name
2025-06-20 11:03:50 +02:00
parent 2be04b6950
commit 5a2426becb
10 changed files with 936 additions and 41 deletions

View File

@@ -0,0 +1,208 @@
#!/usr/bin/env python3
"""
Button-controlled screen mode cycling for ModuleAir Pro 4G
Connected to GPIO6 (GND), cycles through 4 screen modes:
1. Network Status
2. All 4 Sensors (displayAll4_v2)
3. CO2 + PM2.5 + PM10 + Network Status
4. Blank Screen (black)
Usage:
sudo python3 /var/www/moduleair_pro_4g/matrix/button_screen_controller.py
"""
import RPi.GPIO as GPIO
import subprocess
import time
import signal
import sys
import os
# GPIO pin configuration
BUTTON_PIN = 6 # GPIO6, connected to GND when pressed
# Screen mode configuration
SCREEN_MODES = {
0: {
'name': 'Network Status',
'program': '/var/www/moduleair_pro_4g/matrix/screenNetwork/network_status',
'args': ['--led-no-hardware-pulse', '--led-row-addr-type=3', '--led-panel-type=FM6126A']
},
1: {
'name': 'All 4 Sensors',
'program': '/var/www/moduleair_pro_4g/matrix/screenSensors/displayAll4_v2',
'args': ['--led-no-hardware-pulse', '--led-row-addr-type=3', '--led-panel-type=FM6126A']
},
2: {
'name': 'CO2 + PM + Network',
'program': '/var/www/moduleair_pro_4g/matrix/screenSensors/displayCO2_PM_Network',
'args': ['--led-no-hardware-pulse', '--led-row-addr-type=3', '--led-panel-type=FM6126A']
},
3: {
'name': 'Blank Screen',
'program': '/var/www/moduleair_pro_4g/matrix/screenSensors/blank_screen',
'args': ['--led-no-hardware-pulse', '--led-row-addr-type=3', '--led-panel-type=FM6126A']
}
}
class ScreenController:
def __init__(self):
self.current_mode = 0
self.current_process = None
self.running = True
self.last_button_press = 0
self.debounce_time = 0.5 # 500ms debounce
self.last_button_state = True # Button starts as HIGH (pulled up)
# Set up GPIO - clean initialization
try:
# Only cleanup if GPIO was previously initialized
if GPIO.getmode() is not None:
GPIO.cleanup()
except:
pass
GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# Set up signal handlers
signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, self.signal_handler)
print(f"Screen controller initialized. Current mode: {SCREEN_MODES[self.current_mode]['name']}")
print("Using polling method for button detection (more reliable)")
def check_button_pressed(self):
"""Check for button press using polling method"""
current_button_state = GPIO.input(BUTTON_PIN)
# Detect falling edge (button pressed)
if self.last_button_state == True and current_button_state == False:
current_time = time.time()
if current_time - self.last_button_press >= self.debounce_time:
self.last_button_press = current_time
self.cycle_screen_mode()
self.last_button_state = current_button_state
def cycle_screen_mode(self):
"""Cycle to next screen mode"""
print(f"Button pressed! Cycling from mode {self.current_mode}")
# Stop current program
self.stop_current_program()
# Move to next mode
old_mode = self.current_mode
self.current_mode = (self.current_mode + 1) % len(SCREEN_MODES)
print(f"Switching from mode {old_mode} to mode {self.current_mode}: {SCREEN_MODES[self.current_mode]['name']}")
# Start new program
self.start_current_program()
print(f"Mode switch completed successfully")
def stop_current_program(self):
"""Stop the currently running display program"""
if self.current_process:
try:
self.current_process.terminate()
self.current_process.wait(timeout=3)
print("Previous display program terminated")
except subprocess.TimeoutExpired:
self.current_process.kill()
print("Previous display program killed (timeout)")
except Exception as e:
print(f"Error stopping previous program: {e}")
finally:
self.current_process = None
# Kill any remaining matrix display processes (be very specific to avoid killing ourselves)
try:
subprocess.run(['pkill', '-f', '/var/www/moduleair_pro_4g/matrix/screenNetwork/network_status'], check=False)
subprocess.run(['pkill', '-f', '/var/www/moduleair_pro_4g/matrix/screenSensors/displayAll4'], check=False)
subprocess.run(['pkill', '-f', '/var/www/moduleair_pro_4g/matrix/screenSensors/displayCO2'], check=False)
subprocess.run(['pkill', '-f', '/var/www/moduleair_pro_4g/matrix/screenSensors/blank_screen'], check=False)
time.sleep(0.5) # Give processes time to exit
except Exception as e:
print(f"Error killing matrix processes: {e}")
def start_current_program(self):
"""Start the display program for current mode"""
mode_config = SCREEN_MODES[self.current_mode]
program_path = mode_config['program']
if not os.path.exists(program_path):
print(f"Warning: Program {program_path} does not exist")
return
try:
# Build command with arguments
cmd = [program_path] + mode_config['args']
print(f"Starting: {' '.join(cmd)}")
# Start the new program
self.current_process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
preexec_fn=os.setsid
)
print(f"Started {mode_config['name']} (PID: {self.current_process.pid})")
except Exception as e:
print(f"Error starting program {program_path}: {e}")
self.current_process = None
def signal_handler(self, signum, frame):
"""Handle shutdown signals"""
print(f"Received signal {signum}, shutting down...")
print(f"Signal received at frame: {frame}")
self.running = False
self.cleanup()
sys.exit(0)
def cleanup(self):
"""Clean up resources"""
print("Cleaning up...")
self.stop_current_program()
GPIO.cleanup()
def run(self):
"""Main loop"""
# Start with initial screen mode
self.start_current_program()
try:
while self.running:
# Check for button press
self.check_button_pressed()
# Check if current process is still running
if self.current_process and self.current_process.poll() is not None:
print(f"Display program ended unexpectedly (exit code: {self.current_process.returncode})")
self.current_process = None
# Restart the current program
time.sleep(1)
self.start_current_program()
time.sleep(0.1) # Check 10 times per second for responsive button
except KeyboardInterrupt:
print("Keyboard interrupt received")
finally:
self.cleanup()
def main():
"""Main function"""
print("ModuleAir Pro 4G - Button Screen Controller")
print("Press button on GPIO6 to cycle through screen modes")
print("Modes: Network Status -> All 4 Sensors -> CO2+PM+Network -> Blank Screen")
controller = ScreenController()
controller.run()
if __name__ == "__main__":
main()