You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派3B中Systemd服务运行Python脚本无法连接Firebase实时数据库

Hey there! Let's work through your problem and answer each of your questions clearly, plus give you actionable fixes and optimizations.

1. Why isn't the script connecting to Firebase when running as a Systemd service?

The most likely culprits are environment differences between your interactive terminal session and the Systemd service context, or incomplete network readiness even after network-online.target:

  • Minimal environment variables: Your pi user's terminal has a full set of environment variables (like PATH, HOME, or certificate store paths), but Systemd services run with a stripped-down environment by default. Pyrebase might rely on these variables for things like DNS resolution or SSL certificate validation.
  • DNS not fully ready: network-online.target ensures the network is up, but it doesn't always guarantee DNS resolution is fully initialized. Your have_internet() checks connectivity to Google, but Firebase requires resolving its specific domain, which might not be ready when the service starts.
  • Silent stream thread failures: The Firebase stream runs in a background thread. If this thread hits an unhandled exception (like a connection timeout), it might die silently without triggering updates—something you'd see in terminal logs but not in Systemd unless you're logging properly.

2. Do I need service-specific code adjustments?

Absolutely! Here are key tweaks to make your script play nice with Systemd:

  • Handle shutdown signals: Systemd sends SIGTERM when stopping the service—your current script doesn't clean up the Firebase stream or GPIO, which can leave resources hanging.
  • Set explicit working directory: Systemd runs services from / by default. Adding a working directory ensures your script uses its own folder (critical if you ever add relative paths for configs or logs).
  • Log to Systemd journal: Replace print() statements with Python's logging module so you can see output in Systemd logs.

3. Python code optimization suggestions (for beginners)

Let's clean up your code to make it more robust and maintainable:

  • Avoid global variables: The LED1 global is accessed from two threads (stream handler and main loop), which can cause race conditions. Use a threading.Lock to safely update/read the value.
  • Use GPIO context managers: This ensures GPIO pins are cleaned up automatically even if the script crashes.
  • Externalize Firebase config: Don't hardcode API keys in your script—put them in a separate config.json file. This is safer and easier to update without editing code.
  • Add stream reconnection logic: If the Firebase stream drops (e.g., network blip), your script should automatically reconnect instead of stopping.
  • Test Firebase directly: Your have_internet() checks Google, but testing connectivity to your Firebase database URL is more reliable.

Here's a revised version of your code with these changes:

#!/usr/bin/env python3
import RPi.GPIO as GPIO
import pyrebase
import time
import httplib2
import json
import logging
import threading
from signal import signal, SIGTERM, SIGINT

# Set up logging to write to Systemd journal
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Thread-safe LED status with lock
led1_status = "OFF"
status_lock = threading.Lock()

def updateLED(status_LED, adr_LED):
    with status_lock:
        current_state = GPIO.input(adr_LED)
        if status_LED == "ON" and current_state == GPIO.LOW:
            GPIO.output(adr_LED, GPIO.HIGH)
            logger.info("LED turned ON")
        elif status_LED == "OFF" and current_state == GPIO.HIGH:
            GPIO.output(adr_LED, GPIO.LOW)
            logger.info("LED turned OFF")

def have_firebase_connection(database_url):
    # Check connectivity directly to your Firebase database
    db_domain = database_url.split("//")[-1]
    conn = httplib2.HTTPConnectionWithTimeout(db_domain, timeout=5)
    try:
        conn.request("HEAD", "/")
        response = conn.getresponse()
        conn.close()
        return response.status == 200
    except Exception as e:
        logger.error(f"Firebase connection check failed: {str(e)}")
        conn.close()
        return False

def stream_handler(message):
    global led1_status
    new_status = message["data"]
    with status_lock:
        led1_status = new_status
    logger.info(f"Received new status from Firebase: {new_status}")
    updateLED(new_status, LED1_adr)

def cleanup(signum, frame):
    logger.info("Shutting down service...")
    if 'my_stream' in globals():
        my_stream.close()
    GPIO.cleanup()
    exit(0)

# Register signal handlers for Systemd stop commands
signal(SIGTERM, cleanup)
signal(SIGINT, cleanup)

# GPIO setup
LED1_adr = 18
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED1_adr, GPIO.OUT)
GPIO.output(LED1_adr, GPIO.HIGH)

# Wait for Firebase connectivity
logger.info("Waiting for Firebase connection...")
# Load database URL from config first (create config.json with your Firebase settings)
with open("/home/pi/Desktop/FirebaseTest/config.json", "r") as f:
    config = json.load(f)
database_url = config["databaseURL"]

while not have_firebase_connection(database_url):
    GPIO.output(LED1_adr, GPIO.LOW)
    logger.warning("No Firebase connection, retrying...")
    time.sleep(1)
    GPIO.output(LED1_adr, GPIO.HIGH)

# Initialize Firebase
firebase = pyrebase.initialize_app(config)
database = firebase.database()
logger.info("Connected to Firebase successfully")

# Start stream with reconnection logic
my_stream = None
while True:
    try:
        if my_stream is None:
            my_stream = database.child("/led1/status").stream(stream_handler)
            logger.info("Firebase stream started")
        time.sleep(0.25)
        # Update LED in main loop (redundant but safe)
        with status_lock:
            updateLED(led1_status, LED1_adr)
    except Exception as e:
        logger.error(f"Stream error occurred: {str(e)}, reconnecting...")
        if my_stream:
            my_stream.close()
        my_stream = None
        time.sleep(2)

4. Debugging steps to diagnose the issue

Here's how to get to the bottom of why the service isn't working:

  1. Check Systemd logs: Run journalctl -u MyScriptService -f (replace with your service name) to see real-time logs. Look for errors like Firebase connection failures or stream initialization issues.
  2. Compare environments: Temporarily modify your Systemd ExecStart to run /usr/bin/env instead of the script. Check the logs with journalctl to see what environment variables are available—compare this to your terminal's env output to spot missing variables.
  3. Add verbose logging: Update your script to log every critical step (Firebase initialization, stream start, status updates) so you can see where it's getting stuck.
  4. Revise Systemd service config: Use the improved config below to ensure network readiness and proper logging.

Revised Systemd Service Config

Here's an optimized version of your service file:

[Unit]
Description=Firebase LED Controller Service
Wants=network-online.target nss-lookup.target
After=network.target network-online.target nss-lookup.target  # Wait for DNS resolution

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/Desktop/FirebaseTest
ExecStart=/usr/bin/python3 /home/pi/Desktop/FirebaseTest/firebaseTest.py
StandardOutput=journal+console
StandardError=journal+console
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

After modifying the service file, reload Systemd and restart the service:

sudo systemctl daemon-reload
sudo systemctl restart MyScriptService

内容的提问来源于stack exchange,提问作者Jakub Szlaur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:52:02