works reliably - especially when using gunicorn
This commit is contained in:
parent
c937450f92
commit
39cd98823a
7 changed files with 253 additions and 38 deletions
19
README
19
README
|
|
@ -1,19 +0,0 @@
|
|||
verschiedene threads zur auswahl, die vom flask gestartet/gestoppt werden können.
|
||||
|
||||
- statisches bild anzeigen und sich beenden
|
||||
-
|
||||
|
||||
ein task, der die ganze zeit im hintergrund läuft.
|
||||
- statisches bild anzeigen
|
||||
- eine Routine
|
||||
|
||||
ein globales bild. (locking?)
|
||||
die einzelnen
|
||||
|
||||
https://github.com/hzeller/rpi-rgb-led-matrix
|
||||
|
||||
./main.py
|
||||
curl http://localhost:6000/start/helloworld
|
||||
feh --reload 0.15 image.png
|
||||
|
||||
https://pypi.org/project/finnhub-python/
|
||||
139
README.md
Normal file
139
README.md
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# Benutzung
|
||||
|
||||
- als **root** ausführen
|
||||
- ./main.py
|
||||
|
||||
|
||||
verschiedene threads zur auswahl, die vom flask gestartet/gestoppt werden können.
|
||||
|
||||
- statisches bild anzeigen und sich beenden
|
||||
-
|
||||
|
||||
ein task, der die ganze zeit im hintergrund läuft.
|
||||
- statisches bild anzeigen
|
||||
- eine Routine
|
||||
|
||||
ein globales bild. (locking?)
|
||||
die einzelnen
|
||||
|
||||
https://github.com/hzeller/rpi-rgb-led-matrix
|
||||
|
||||
./main.py
|
||||
curl http://localhost:6000/start/helloworld
|
||||
feh --reload 0.15 image.png
|
||||
|
||||
https://pypi.org/project/finnhub-python/
|
||||
|
||||
# Locale
|
||||
|
||||
Für deutsche Ausgabe (z.B. den Monatsnamen):
|
||||
- als root: `dpkg-reconfigure locales`
|
||||
- **de_DE.UTF-8** aktivieren
|
||||
|
||||
# Flackern
|
||||
|
||||
## Erkenntnisse
|
||||
|
||||
"Hardware pulsing" macht zwar ein ruhiges Bild aber viele Artefakte.
|
||||
Wenn man "hardware pulsing" abschaltet, muss die Software die Ansteuerung der
|
||||
Panels machen. Das ist für den Raspi stressiger.
|
||||
|
||||
Am besten startet man den Webserver mit:
|
||||
|
||||
chrt -f 20 ./main.py
|
||||
|
||||
Dann werden Hintergrundprozesse unterdrückt und Artefakte vermieden.
|
||||
|
||||
Interessante Optionen:
|
||||
|
||||
- `options.gpio_slowdown`: Mehr=mehr Flackern. Weniger=Artifakte/Rauschen im linken Bereich.
|
||||
- `options.pwm_bits`: Mehr=mehr Flackern. Weniger=weniger Helligkeitsabstufungen
|
||||
|
||||
## Einstellung 1
|
||||
|
||||
- kaum sichtbare Artefakte
|
||||
- ca. 10 Hz Bildwiederholrate
|
||||
- 4 Panels
|
||||
|
||||
```py
|
||||
options = RGBMatrixOptions()
|
||||
options.rows = MATRIX_HEIGHT
|
||||
options.cols = MATRIX_WIDTH
|
||||
options.chain_length = NUMBER_OF_PANELS
|
||||
options.parallel = 1
|
||||
options.hardware_mapping = 'regular' # change if using Adafruit HAT, e.g. 'adafruit-hat'
|
||||
options.gpio_slowdown = 10 # try 2, 4, or even 5
|
||||
options.drop_privileges = False
|
||||
options.pwm_bits = 10 # default 11–12; lower = faster refresh
|
||||
```
|
||||
|
||||
## Einstellung 2
|
||||
|
||||
- leichte Artifakte, hält 10 Minuten durch
|
||||
- ca. 30 Hz Bildwiederholrate
|
||||
- 3 Panels
|
||||
|
||||
```py
|
||||
options.hardware_mapping = 'regular' # change if using Adafruit HAT, e.g. 'adafruit-hat'
|
||||
options.gpio_slowdown = 6 # try 2, 4, or even 5
|
||||
options.disable_hardware_pulsing = True
|
||||
#options.show_refresh_rate = True
|
||||
options.drop_privileges = False
|
||||
```
|
||||
|
||||
## Einstellung 3
|
||||
|
||||
- kaum Artefakte
|
||||
- ca. 30 Hz
|
||||
- 3 Panels
|
||||
|
||||
```py
|
||||
options.hardware_mapping = 'regular' # change if using Adafruit HAT, e.g. 'adafruit-hat'
|
||||
options.gpio_slowdown = 7 # try 2, 4, or even 5
|
||||
options.disable_hardware_pulsing = True
|
||||
options.drop_privileges = False
|
||||
options.pwm_bits = 10 # default 11–12; lower = faster refresh
|
||||
```
|
||||
|
||||
## Einstellung 4
|
||||
|
||||
- 20-30 Hz
|
||||
- 3 Panels
|
||||
- keine Artefakte
|
||||
|
||||
```py
|
||||
options.hardware_mapping = 'regular' # change if using Adafruit HAT, e.g. 'adafruit-hat'
|
||||
options.gpio_slowdown = 8 # try 2, 4, or even 5
|
||||
options.disable_hardware_pulsing = True
|
||||
options.drop_privileges = False
|
||||
|
||||
## Einstellung 5
|
||||
|
||||
- 50 Hz
|
||||
- 4 Panels
|
||||
- keine Artefakte
|
||||
- hält es langfristig damit durch?
|
||||
|
||||
options.hardware_mapping = 'regular' # change if using Adafruit HAT, e.g. 'adafruit-hat'
|
||||
options.gpio_slowdown = 4 # try 2, 4, or even 5
|
||||
options.disable_hardware_pulsing = True
|
||||
options.drop_privileges = False
|
||||
options.pwm_bits = 5 # default 11–12; lower = faster refresh
|
||||
|
||||
|
||||
# Links
|
||||
|
||||
- https://github.com/hzeller/rpi-rgb-led-matrix
|
||||
|
||||
# TODO
|
||||
|
||||
Das ganze als minimalen Case ohne Multithreading und Klassen.
|
||||
Ist das Geflacker dann weg?
|
||||
|
||||
# 2026-01-16
|
||||
|
||||
- git pull
|
||||
- spannung auf 5,01 Volt reduziert (von 5,2 Volt)
|
||||
- gpio-slowdown auf 4
|
||||
- nach dem stop (ctrl-c) und start 10 sekunden warten
|
||||
- mit "gunicorn -w 1 -k gthread main:app" laufen lassen
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
from rgbmatrix import RGBMatrix, RGBMatrixOptions
|
||||
from PIL import ImageFont, Image
|
||||
#from time import sleep
|
||||
|
||||
# Das Bild nur speichern. Nicht die LED-Matrix ansteuern.
|
||||
DEV = False
|
||||
|
|
@ -18,22 +19,39 @@ class MyMatrix:
|
|||
|
||||
try:
|
||||
self.font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-ExtraLight.ttf", 18)
|
||||
self.large_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-ExtraLight.ttf", 32)
|
||||
except IOError:
|
||||
self.font = ImageFont.load_default()
|
||||
self.large_font = ImageFont.load_default()
|
||||
|
||||
self.width = MATRIX_WIDTH * NUMBER_OF_PANELS
|
||||
self.height = MATRIX_HEIGHT
|
||||
|
||||
if not DEV:
|
||||
options = RGBMatrixOptions()
|
||||
options.rows = MATRIX_HEIGHT
|
||||
options.cols = MATRIX_WIDTH
|
||||
options.chain_length = NUMBER_OF_PANELS
|
||||
options.parallel = 1
|
||||
options.hardware_mapping = 'regular' # change if using Adafruit HAT, e.g. 'adafruit-hat'
|
||||
options.gpio_slowdown = 5 # try 2, 4, or even 5
|
||||
options.disable_hardware_pulsing = True
|
||||
#options.parallel = 1
|
||||
#options.hardware_mapping = 'regular' # change if using Adafruit HAT, e.g. 'adafruit-hat'
|
||||
options.gpio_slowdown = 4 # try 2, 4, or even 5
|
||||
#options.disable_hardware_pulsing = True
|
||||
#options.show_refresh_rate = True
|
||||
options.drop_privileges = False
|
||||
options.brightness = 60 # scale down brightness if needed
|
||||
options.pwm_bits = 8 # default 11–12; lower = faster refresh
|
||||
|
||||
self.matrix = RGBMatrix(options=options)
|
||||
|
||||
#sleep(0.2)
|
||||
#self.matrix.Clear()
|
||||
#sleep(0.2)
|
||||
#self.matrix.Clear()
|
||||
#sleep(0.2)
|
||||
#self.matrix.Clear()
|
||||
#sleep(0.2)
|
||||
|
||||
|
||||
def show(self, image):
|
||||
"""Save the image to disk in DEV mode. Or send it to the LED matrix panel."""
|
||||
if DEV:
|
||||
|
|
|
|||
12
main.py
12
main.py
|
|
@ -15,6 +15,7 @@ scenes = {}
|
|||
for file in os.listdir(SCENES_DIR):
|
||||
if file.endswith(".py") and file != "__init__.py":
|
||||
module_name = file[:-3]
|
||||
print(f"Loading scene: {module_name}")
|
||||
module = importlib.import_module(f"{SCENES_DIR}.{module_name}")
|
||||
scenes[module_name] = module
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ app = Flask(__name__)
|
|||
# Thread and control event
|
||||
animation_thread = None
|
||||
stop_event = Event()
|
||||
image_buffer = Image.new("RGB", (128, 64))
|
||||
#image_buffer = Image.new("RGB", (128, 64))
|
||||
|
||||
# Example background animation function
|
||||
def run_animation(name):
|
||||
|
|
@ -50,7 +51,6 @@ def start_animation(scene_name):
|
|||
|
||||
# Reset stop event and start new thread
|
||||
stop_event.clear()
|
||||
#animation_thread = Thread(target=scenes[scene_name].start, args=(image_buffer, stop_event), daemon=True)
|
||||
animation_thread = Thread(target=scenes[scene_name].start, args=(stop_event,), daemon=True)
|
||||
animation_thread.start()
|
||||
|
||||
|
|
@ -59,6 +59,11 @@ def start(animation_name):
|
|||
start_animation(animation_name)
|
||||
return jsonify({"status": "started", "animation": animation_name})
|
||||
|
||||
@app.route("/reset")
|
||||
def reset():
|
||||
lib.display.matrix.matrix.Clear()
|
||||
return "ok"
|
||||
|
||||
@app.route("/stop")
|
||||
def stop():
|
||||
global animation_thread, stop_event
|
||||
|
|
@ -68,5 +73,6 @@ def stop():
|
|||
return jsonify({"status": "stopped"})
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True, port=6000)
|
||||
app.run(port=6000)
|
||||
#app.run(debug=True, port=6000)
|
||||
|
||||
|
|
|
|||
39
scenes/clock.py
Normal file
39
scenes/clock.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import io
|
||||
from time import sleep
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import lib.display
|
||||
|
||||
import datetime
|
||||
import locale
|
||||
locale.setlocale(locale.LC_TIME, "de_DE.UTF-8")
|
||||
|
||||
def start(stop_event, text="Hello World!"):
|
||||
image_buffer = lib.display.matrix.buffer()
|
||||
draw = ImageDraw.Draw(image_buffer)
|
||||
while not stop_event.is_set():
|
||||
# All to black
|
||||
draw.rectangle( ((0,0),(lib.display.matrix.width-1,lib.display.matrix.height-1)), fill=(0,50,0) )
|
||||
#draw.rectangle( ((0,0),(lib.display.matrix.width-1,lib.display.matrix.height-1)), fill=(255,255,255) )
|
||||
|
||||
# Current time
|
||||
now = datetime.datetime.now()
|
||||
#now_string = now.strftime('%d. %B %Y – %H:%M:%S')
|
||||
# Date into top left
|
||||
draw.text((0, 0), now.strftime('%d. %B %Y'), fill=(255, 255, 0), font=lib.display.matrix.font)
|
||||
# Time into middle
|
||||
draw.text((20, 20), now.strftime('%H:%M:%S'), fill=(0, 255, 255), font=lib.display.matrix.large_font)
|
||||
|
||||
float_seconds = now.second + now.microsecond/10**6
|
||||
length = 64*4/60.0*float_seconds
|
||||
# 64*4 = 60
|
||||
# x = seconds
|
||||
# x=64*4/60*seconds
|
||||
#draw.line( ((0,63), (now.second,63)), fill=(128,128,128), width=1)
|
||||
draw.line( ((0,62), (length,62)), fill=(128,128,128), width=2)
|
||||
|
||||
lib.display.matrix.show(image_buffer)
|
||||
|
||||
sleep(0.05)
|
||||
|
||||
32
scenes/colorclock.py
Normal file
32
scenes/colorclock.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import io
|
||||
from time import sleep
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import lib.display
|
||||
|
||||
import datetime
|
||||
import locale
|
||||
locale.setlocale(locale.LC_TIME, "de_DE.UTF-8")
|
||||
|
||||
def start(stop_event, text="Hello World!"):
|
||||
while not stop_event.is_set():
|
||||
image_buffer = lib.display.matrix.buffer()
|
||||
draw = ImageDraw.Draw(image_buffer)
|
||||
now = datetime.datetime.now()
|
||||
now_string = now.strftime('%d. %B %Y – %H:%m:%S')
|
||||
draw.text((0, 0), now_string, fill=(255, 255, 0), font=lib.display.matrix.font)
|
||||
draw.text((0, 20), now_string, fill=(0, 255, 255), font=lib.display.matrix.font)
|
||||
draw.text((0, 40), now_string, fill=(255, 0, 255), font=lib.display.matrix.font)
|
||||
float_seconds = now.second + now.microsecond/10**6
|
||||
length = 64*4/60.0*float_seconds
|
||||
# 64*4 = 60
|
||||
# x = seconds
|
||||
# x=64*4/60*seconds
|
||||
#draw.line( ((0,63), (now.second,63)), fill=(128,128,128), width=1)
|
||||
draw.line( ((0,62), (length,62)), fill=(128,128,128), width=2)
|
||||
|
||||
lib.display.matrix.show(image_buffer)
|
||||
|
||||
sleep(0.05)
|
||||
|
||||
|
|
@ -3,21 +3,21 @@
|
|||
import io
|
||||
from time import sleep
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
#from lib import display
|
||||
import lib.display
|
||||
|
||||
def start(stop_event, text="Hello World!"):
|
||||
t = 0
|
||||
#t = 0
|
||||
image_buffer = lib.display.matrix.buffer()
|
||||
draw = ImageDraw.Draw(image_buffer)
|
||||
draw.rectangle( ((0,0),(lib.display.matrix.width-1,lib.display.matrix.height-1)), fill=(0,0,90) )
|
||||
draw.text((0, 0), "Hello World", fill=(255, 255, 255), font=lib.display.matrix.font)
|
||||
#draw.text((0, 0), f"Hello World {t}", fill=(255, 255, 255), font=lib.display.matrix.font)
|
||||
|
||||
#t += 1
|
||||
#print(t)
|
||||
|
||||
lib.display.matrix.show(image_buffer)
|
||||
while not stop_event.is_set():
|
||||
#image_buffer = Image.new("RGB", (lib.display.matrix_width, lib.display.matrix_height))
|
||||
image_buffer = lib.display.matrix.buffer()
|
||||
draw = ImageDraw.Draw(image_buffer)
|
||||
draw.text((0, 0), f"Hello World {t}", fill=(255, 255, 255), font=lib.display.matrix.font)
|
||||
|
||||
t += 1
|
||||
print(t)
|
||||
|
||||
lib.display.matrix.show(image_buffer)
|
||||
|
||||
#sleep(0.2)
|
||||
sleep(1)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue