Interrupt mit Bottle / LED Steuerung

Django, Flask, Bottle, WSGI, CGI…
Antworten
Kalle322
User
Beiträge: 3
Registriert: Montag 8. Oktober 2018, 19:32

Hallo Community,

ich möchte eine LED Stripes (WS2801) die an die IOs von einem Raspberry angeschlossen ist, per http aufrufe steuern. Dazu habe ich python und bottle installiert. Das schalten per Webaufruf funktioniert ohne Probleme, jedoch habe ich nun das Problem, dass der Aufruf erst komplett abgearbeitet werden muss bevor der nächster Aufruf abgearbeitet wird. Ich möchte jedoch direkt auf die Änderungen reagieren. Nun die Frage, ist es möglich interrupts in Verbindung mit bottle zu programmieren damit immer der letzte http Aufruf abgearbeitet wird?

hier der bisherige code:

Code: Alles auswählen

from bottle import route, run, template

# Simple demo of of the WS2801/SPI-like addressable RGB LED lights.
import time
import RPi.GPIO as GPIO

# Import the WS2801 module.
import Adafruit_WS2801
import Adafruit_GPIO.SPI as SPI


# Configure the count of pixels:
PIXEL_COUNT = 160

# Alternatively specify a hardware SPI connection on /dev/spidev0.0:
SPI_PORT   = 0
SPI_DEVICE = 0
pixels = Adafruit_WS2801.WS2801Pixels(PIXEL_COUNT, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE), gpio=GPIO)

# Define the wheel function to interpolate between different hues.
def wheel(pos):
    if pos < 85:
        return Adafruit_WS2801.RGB_to_color(pos * 3, 255 - pos * 3, 0)
    elif pos < 170:
        pos -= 85
        return Adafruit_WS2801.RGB_to_color(255 - pos * 3, 0, pos * 3)
    else:
        pos -= 170
        return Adafruit_WS2801.RGB_to_color(0, pos * 3, 255 - pos * 3)

def blink_color(pixels, blink_times=5, wait=0.5, speed=0.08, red=255, blue=0, green=0):
    for i in range(blink_times):
        # blink two times, then wait
        pixels.clear()
        for j in range(2):
            for k in range(pixels.count()):
                pixels.set_pixel(k, Adafruit_WS2801.RGB_to_color( red, blue, green ))
            pixels.show()
            time.sleep(speed)
            pixels.clear()
            pixels.show()
            time.sleep(speed)
        time.sleep(wait)

def rainbow_cycle(pixels, wait=0.005):
    for j in range(256): # one cycle of all 256 colors in the wheel
        for i in range(pixels.count()):
            pixels.set_pixel(i, wheel(((i * 256 // pixels.count()) + j) % 256) )
        pixels.show()
        if wait > 0:
            time.sleep(wait)

@route('/')
@route('/blink/<blink_times:int>/<wait:float>/<speed:float>/<red:int>/<blue:int>/<green:int>')
def blink(blink_times='2',wait='0.5',speed='0.08',red='255',blue='0',green='0'):
        pixels.clear()
        pixels.show()
        blink_color(pixels, blink_times = blink_times, wait = wait, speed = speed, red = red, blue = blue, green = green)

@route('/rainbow/<wait:float>')
def rainbow(wait='0.1'):
        pixels.clear()
        pixels.show()
        rainbow_cycle(pixels, wait = wait)

run(host='localhost', port=8080, debug=True)
Später möchte ich, dass die http Aufrufe in einer Schleife laufen und per nächster Http Aufruf beendet werden kann.

Ich freue mich über Anregungen und Lösungsansätze.

Vielen Dank und Viele Grüße
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bottle ist dafür nur bedingt geeignet. Denn bottle is synchron, ein request muss immer ein response erzeugen, und das ziemlich schnell. Sonst hängt dein Browser. Und im übrigen verlangt der das auch so, auch JavaScript kennt keine Threads.

Womit wir bei einer Lösung wären: verlager die LED Steuerung in einen Thread. Der bekommt dann von den HTTP-Aufrufen seine Anweisungen.

Alternativ kannst du tornado statt bottle verwenden. Und mit timer-basierten tasks arbeiten.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@__deets__: das hat nichts mit bottle zu tun. Für bottle gibt es auch threaded dispatcher, z.B. mit unicorn, daneben gibts es auch noch gevent.

@Kalle322: Am besten benutzt Du einen Thread, den Du über eine Queue mit den Befehlen versorgst, die dann abgearbeitet werden sollen.
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Sirus3: natürlich hat das mit bottle zu tun, denn das geht anders als zB Tornado nicht davon aus, das man mit einem mainloop hantieren muss, in den man zeitgesteuerte Aufgaben hängen kann. Und daher dafür auch keine API mitbringt. Weswegen sich dein Vorschlag ja auch mit meinem deckt... 🙄

Das man mit gunicorn etc den Durchsatz steigern kann, ist bei einer Heimanwendung auf dem PI ja wohl eher irrelevant....
Kalle322
User
Beiträge: 3
Registriert: Montag 8. Oktober 2018, 19:32

Habe mich versucht in Threading einzulesen.

In der Theorie würde ich es so versuchen, dass ich die Funktionen der LEDs in eine Dauerschleife packe. In der Dauerschleife baue ich ein Switch/Case Konstrukt ein welches per globale Variablen gesteuert wird. Mit den Http Aufrufe würde ich dann die Globalen Variablen ändern.

Könnte es so funktionieren oder habt ihr eine bessere Idee?
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das geht, ja. Eine queue wie von Sirius3 vorgeschlagen ist eine Idee, weil man damit sowohl Thread sicher arbeitet, als auch per timeout auf dem get-call gleich die Timer-Frequenz bestimmen kann.
Kalle322
User
Beiträge: 3
Registriert: Montag 8. Oktober 2018, 19:32

Könnte mir Jemand hierfür ein Beispiel zeigen, damit ich meinen Code eventuell daran anpassen könnte?

Wäre demjenigen sehr sehr dankbar.

Viele Grüße
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

@Kalle322: Hast du es zwischenzeitlich schon versucht? Klingt nach keiner zu komplexen Sache, den Thread startest du in einer main()-Funktion, wo du auch dein Queue-Objekt instanziierst. Damit die Queue befüllt wird, wirst du eine Schleife benötigen.
When we say computer, we mean the electronic computer.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Kalle322:
Du musst dir dafür im Code merken, ob aktuell eine Anfrage verarbeitet wird (Blinken / Rainbow). Diese muss bei einer neuen Anfrage abgebrochen werden, damit sofort die nächste Verarbeitung starten kann. Das ist doch, was du willst, oder? Man regelt das über Threads und ein Event, welches beim Eintreffen einer neuen Anfrage gesetzt werden sollte. Dies musst du jeweils in die beiden Schleifen einbauen und zu Beginn eines Durchlaufs den Status des besagten Events abfragen (d.h. ob abgebrochen werden soll). Lies dich hierzu am Besten erstmal ins Thema Threading ein. Dafür gibt es viele Tutorials.
Antworten