Python Script LED Uhr

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Snop007@web.de
User
Beiträge: 1
Registriert: Samstag 14. September 2024, 17:26

Hallo in die Runde,

ich bin Neuling was die Python-Programierung angeht. Mein erstes Projekt sollte ein am Raspberrry angeschlossener LED-Stripe WS2812B mit 60 LEDs (ohne extra Stromzufuhr (da nur bei 10-20% Brightness) sein.
Das Script läuft auch schon, auch in der vrtuellen Umgebung und in CRON eingebunden.

Leider gibt es zwischenzeitlich allerdings im Betrieb das merkwürdige verhalten, dass die aktuelle Uhrzeit nicht richtig angezeigt wird.
Gedane war:
1. beim starten des Scipts blinkt die (0) Led dreimal blau --> funktionier
2. Beim Scriptstart holt er sich die aktuelle Zeit und schaltet die grünen LEDs ein, die bereits leuchten sollen
3. Alle 7 Minuten erleuchtet eine LED in grün hinzu -> es funktioniert, das alle 7 Minuten eine hinzuwächst
4. Die LEDS, welche die vollen Stunden deutlich machen, leuchten dauerhaft in weiß --> funktioniert super

Außerdem habe ich die Funktion eingefügt, dass ich die Zeitdauer, in die Uhr laufen soll z.B. 8 bis 16 Uhr, definieren kann --> hier scheint es noch nicht richtig programiert zu sein.

Kann mir da eventuell jemand einen Tipp geben, was ich falsch habe:
Würde mir sehr helfen.

Code: Alles auswählen

#!/usr/bin/env python3
import time
from rpi_ws281x import Adafruit_NeoPixel, Color  # Corrected import
from datetime import datetime, timedelta

# LED strip configuration:
LED_COUNT      = 60       # Number of LED pixels.
LED_PIN        = 18       # GPIO pin connected to the pixels (18 uses PWM!).
LED_FREQ_HZ    = 800000   # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10       # DMA channel to use for generating a signal (try 10)
LED_BRIGHTNESS = 10       # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False    # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0        # Set to '1' for GPIOs 13, 19, 41, 45 or 53
COLOR_GREEN    = Color(0, 255, 0)  # Green color
COLOR_BLUE     = Color(0, 0, 255)  # Blue color
COLOR_WHITE    = Color(255, 255, 255)  # White color

# Constants for the time-based LED clock
START_HOUR = 9  # Start of the LED clock (9 AM)
END_HOUR = 16   # End of the LED clock (4 PM)
MINUTES_PER_LED = 7  # 7 minutes per LED
SECONDS_PER_LED = MINUTES_PER_LED * 60  # 7 minutes in seconds
TOTAL_HOURS = END_HOUR - START_HOUR  # Total duration in hours (7 hours)

# Initialize LED strip
def init_strip():
    strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
    strip.begin()
    return strip

# Function to calculate the number of LEDs that should be on based on the current time
def calculate_active_leds():
    now = datetime.now()
    current_hour = now.hour
    current_minute = now.minute

    # Check if the current time is within the range of 9 AM to 4 PM
    if current_hour < START_HOUR or current_hour >= END_HOUR:
        return 0  # No LEDs should be lit outside this time range

    # Calculate the total minutes since 9 AM
    total_minutes_passed = (current_hour - START_HOUR) * 60 + current_minute
    total_leds_passed = total_minutes_passed // MINUTES_PER_LED

    # Ensure we don't go beyond the total number of LEDs
    return min(total_leds_passed, LED_COUNT)

# Function to set the full-hour LEDs to white from the start
def set_full_hour_leds_to_white(strip):
    # Full hours are at the given indices for full-hour marks
    full_hour_leds = [0, 9, 18, 24, 32, 40, 49, 57, ]  # Full-hour LED indices
    for led in full_hour_leds:
        strip.setPixelColor(led, COLOR_WHITE)  # Set each full-hour LED to white
    strip.show()

# Function to blink the first LED three times in blue
def blink_blue_led(strip, blink_count=3):
    for _ in range(blink_count):
        strip.setPixelColor(0, COLOR_BLUE)  # Set first LED to blue
        strip.show()  # Update the LED
        time.sleep(0.5)  # Wait for 0.5 seconds
        strip.setPixelColor(0, Color(0, 0, 0))  # Turn off the LED
        strip.show()  # Update the LED
        time.sleep(0.5)  # Wait for 0.5 seconds

# Function to turn on LEDs progressively like a clock without full-hour white LEDs
def clock_progress(strip):
    interval_duration = SECONDS_PER_LED

    # Turn on already elapsed LEDs
    active_leds = calculate_active_leds()
    for i in range(active_leds):
        strip.setPixelColor(i, COLOR_GREEN)  # Turn on the LED in green
    strip.show()  # Update the LEDs to reflect the already active ones

    # Continue lighting the next LEDs at the appropriate interval
    for i in range(active_leds, LED_COUNT - 1):  # Leave the last LED for the blue color
        # Ensure we are within the time range
        now = datetime.now()
        if now.hour >= END_HOUR:
            break  # Stop once we hit 4 PM

        time.sleep(interval_duration)  # Wait for 7 minutes before lighting the next LED

        strip.setPixelColor(i, COLOR_GREEN)  # Turn on the next LED in green

        strip.show()  # Update the LEDs

# Main program logic follows:
if __name__ == '__main__':
    # Create NeoPixel object with appropriate configuration.
    strip = init_strip()

    print('Press Ctrl-C to quit.')

    try:
        # Blink the first LED in blue three times at startup
        blink_blue_led(strip)
        
         # Set full-hour LEDs to white initially
        set_full_hour_leds_to_white(strip) 

        # Run the clock progress function
        clock_progress(strip)  # Light up LEDs progressively from 9 AM to 4 PM

    except KeyboardInterrupt:
        # Clear the strip if interrupted
        for i in range(LED_COUNT):
            strip.setPixelColor(i, Color(0, 0, 0))  # Turn off all LEDs
        strip.show()

Sirius3
User
Beiträge: 18111
Registriert: Sonntag 21. Oktober 2012, 17:20

Was genau funktioniert nicht so, wie Du Dir das vorstellst?

60 ist ja nicht durch 7 teilbar, Stunden und LEDs können so also nicht übereinstimmen.
Die LED-Stunden müßten daher ungefähr bei [0, 9, 18, 26, 35, 43, 52, 61] sein.
Benutzeravatar
__blackjack__
User
Beiträge: 13758
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Snop007@web.de: Anmerkungen zum Quelltext: `timedelta` wird importiert, aber nirgends verwendet.

Das Hauptprogramm sollte in einer Funktion stehen. Die heisst üblicherweise `main()`.

Da sind zu viele Kommentare. Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Auch problematisch sind Werte aus dem Code die in Kommentaren wiederholt werden. Die Uhrzeiten werden beispielsweise oben im Code als Konstanten definiert, stehen dann aber noch mal mehrfach über den Code verteilt in Kommentaren. Wenn man mal die Konstanten ändert, ist es sehr wahrscheinlich das man nicht immer daran denkt alle betroffenen Kommentare anzupassen und dann hat man falsche Kommentare, was noch schlimmer ist als etwas nicht zu kommentieren, denn nun weiss der Leser nicht was falsch ist — der Kommentar oder der Code.

Noch leichter zu übersehen/finden ist es wenn eine Zahl im Code, wie das 3× blinken in Kommentaren dann als Zahlwort („three“) steht. Und das nicht nur direkt bei der Funktion, sondern auch dort wo sie aufgerufen wird, wo aber nirgends eine 3 zu sehen ist. Auch hier ist die Wahrscheinlichkeit das Code und Kommentare sich widersprechen wenn man da mal was ändert, sehr hoch.

Die Kommentare über den Funktionen sollten eher Docstrings sein. In dem Kommentar/Docstring muss man nicht noch mal erwähnen, dass es sich um eine Funktion handelt. Das sieht man ja schon am Code.

`TOTAL_HOURS` wird definiert, aber nirgends verwendet.

Warum wird in `clock_progress()` `SECONDS_PER_LED` in `interval_duration` ”umbenannt”?

Wenn man zwei Vergleiche gegen die selbe Variable verknüpft, kann man in der Regel die Operatoren verketten und den Ausdruck damit vereinfachen.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import time
from datetime import datetime as DateTime

from rpi_ws281x import Adafruit_NeoPixel, Color

#
# LED strip configuration.
#
LED_COUNT = 60
LED_PIN = 18
LED_FREQ_HZ = 800_000
LED_DMA = 10
LED_BRIGHTNESS = 10  # 0 to 255.
LED_INVERT = False  # invert the signal when using NPN transistor level shift
LED_CHANNEL = 0  # Set to 1 for GPIOs 13, 19, 41, 45 or 53

COLOR_BLACK = Color(0, 0, 0)
COLOR_GREEN = Color(0, 255, 0)
COLOR_BLUE = Color(0, 0, 255)
COLOR_WHITE = Color(255, 255, 255)

START_HOUR = 9
END_HOUR = 16
MINUTES_PER_LED = 7
SECONDS_PER_LED = MINUTES_PER_LED * 60


def init_strip():
    """
    Initialize and return the LED strip.
    """
    strip = Adafruit_NeoPixel(
        LED_COUNT,
        LED_PIN,
        LED_FREQ_HZ,
        LED_DMA,
        LED_INVERT,
        LED_BRIGHTNESS,
        LED_CHANNEL,
    )
    strip.begin()
    return strip


def blink_blue_led(strip):
    """
    Blink the first LED three times in blue.
    """
    for _ in range(3):
        strip.setPixelColor(0, COLOR_BLUE)
        strip.show()
        time.sleep(0.5)
        strip.setPixelColor(0, COLOR_BLACK)
        strip.show()
        time.sleep(0.5)


def set_full_hour_leds_to_white(strip):
    for full_hour_led in [0, 9, 18, 24, 32, 40, 49, 57]:
        strip.setPixelColor(full_hour_led, COLOR_WHITE)
    strip.show()


def calculate_active_leds():
    """
    Calculate the number of LEDs that should be on, based on the current time.
    """
    now = DateTime.now()

    if not START_HOUR <= now.hour <= END_HOUR:
        return 0

    minutes_passed = (now.hour - START_HOUR) * 60 + now.minute
    leds_passed = minutes_passed // MINUTES_PER_LED
    return min(leds_passed, LED_COUNT)


def clock_progress(strip):
    """
    Turn on LEDs progressively like a clock without full-hour white LEDs.
    """
    #
    # Turn on already elapsed LEDs.
    #
    active_leds = calculate_active_leds()
    for led in range(active_leds):
        strip.setPixelColor(led, COLOR_GREEN)
    strip.show()
    #
    # Continue lighting the next LEDs at the appropriate interval.
    #
    # Leave the last LED for the blue color.
    #
    for led in range(active_leds, LED_COUNT - 1):
        if DateTime.now().hour >= END_HOUR:
            break

        time.sleep(SECONDS_PER_LED)
        strip.setPixelColor(led, COLOR_GREEN)
        strip.show()


def main():
    strip = init_strip()
    print("Press Ctrl-C to quit.")
    try:
        blink_blue_led(strip)
        set_full_hour_leds_to_white(strip)
        clock_progress(strip)

    except KeyboardInterrupt:
        for i in range(LED_COUNT):
            strip.setPixelColor(i, COLOR_BLACK)
        strip.show()


if __name__ == "__main__":
    main()
Das die vollen Stunden dauerhaft weiss leuchten glaube ich nicht. Da ist nichts im Code was verhindern würde, dass die durch grün ersetzt werden können.

Ich finde den Ansatz auch nicht so gut immer eine gewisse Zeit zu schlafen und dann einfach die nächste LED anzuschalten. Auch wenn die Ungenautigkeiten, die sich da bei 7-Minuten-Schlafen aufsummieren, sehr klein sind, würde ich eher das eher so schreiben, dass alle x Sekunden der komplette Strip anhand der aktuellen Uhrzeit berechnet wird.

„[H]ier scheint es noch nicht richtig programiert zu sein“ ist keine besonders aussagekräftige Fehlerbeschreibung.
“The city's central computer told you? R2D2, you know better than to trust a strange computer!” — C3PO
Antworten