Dieses Tutorial erweitert das vorherige Tutorial, So bauen Sie ein Tweet-gesteuertes RGB-LCD, durch Hinzufügen einer Webseitensteuerung. Hiermit können Sie die Konfiguration der Tweetbox schnell von Ihrem Laptop, Tablet oder Telefon aus ändern.
In diesem Lernprogramm lernen Sie nützliche Techniken, mit denen Sie zu Ihren eigenen Raspberry Pi-Projekten eine Webseitensteuerung hinzufügen können.
Sudo Pip installieren Tornado
Den gesamten Code für dieses Projekt finden Sie hier:
https://github.com/jerbly/tutorials/tree/master/tweetbox
Um es noch einmal zusammenzufassen: Am Ende des vorherigen Tutorials hatten Sie einen Raspberry Pi mit RGB-LCD, der an Twitter angeschlossen war, und empfing öffentliche Tweets, die einem Filter entsprechen. Die RGB-Hintergrundbeleuchtung ändert sich in Abhängigkeit von anderen übereinstimmenden Wörtern im Tweet. Um den Filter und die Farbzuordnung zu konfigurieren, mussten Sie den Code ändern und das Programm erneut starten.
In dieser ersten Phase füge ich dem Programm einen Webserver hinzu. Dann können Sie auf eine Webseite gehen und die Konfiguration live aktualisieren. Ich verwende Tornado, es gibt viele verschiedene Web-Frameworks für Python, und ich habe viele von ihnen im Laufe der Jahre verwendet, aber dies ist jetzt meine gehe zu Rahmen. Es gibt viele Kästchen für viele verschiedene Projekte.
Schauen Sie sich das an Hallo Welt Beispiel auf der Tornado-Seite, damit Sie sehen, wie Sie einen Handler einrichten und einen Tornado-Server-Thread starten. Ich werde hier genau dasselbe Prinzip anwenden.
Ich nehme die tweetbox.py
Code aus dem vorherigen Tutorial und fügen Sie das Web-Framework hinzu. Zunächst habe ich nur ein einfaches Formular, mit dem wir ändern können, was ich auf Twitter verfolge. Fügen Sie zunächst einige Importe oben im Skript hinzu:
import tornado.ioloop import tornado.web
Als nächstes brauche ich zwei Handler: einen, um das HTML-Formular anzuzeigen, und einen, um die Formularübermittlung zu erhalten:
Klasse MainHandler (tornado.web.RequestHandler): def get (self): self.render ("templates / form1.html") Klasse ConfigHandler (tornado.web.RequestHandler): def post (selbst): config_track = self.get_argument ( "config_track") restart (config_track) self.write ("Jetzt% s verfolgen"% config_track) application = tornado.web.Application ([(r "/", MainHandler), (r "/ config", ConfigHandler),] )
Beachten Sie, dass MainHandler
Verwendet erhalten
und ConfigHandler
Verwendet Post
, Das HTML-Formular verwendet die Post-Methode, um die Formulardaten zurück an den Webserver zu senden. Um dem Formular zu dienen, dient das MainHandler
Ruft einfach die Renderfunktion auf, damit eine Vorlage gerendert und an den Browser gesendet wird. Mehr dazu gleich.
Wenn die Formulardaten zurückkommen ConfigHandler
extrahiert ein Argument, config_track
. Dies wird an a gesendet Neustart
Mit dieser Funktion können Sie die Einstellungen mit tweepy ändern, bevor Sie einen einfachen String zurückgeben, der anzeigt, was verfolgt wird.
Erstellen Sie die Vorlage, indem Sie ein Verzeichnis hinzufügen Vorlagen in das Quellverzeichnis und erstellen Sie das form1.html
Datei dort:
Himbeer-Pi-Tweetbox
Dies ist ein sehr einfaches Formular mit einem Texteingabefeld und einer Schaltfläche zum Senden. Es ist alles, was zu diesem Zeitpunkt benötigt wird. Als nächstes erstellen Sie die Neustart
Funktion:
def restart (track_text): stream.disconnect () time.sleep (5) #Erlaubt dem Thread, die Verbindung zu trennen… stream.filter (track = [track_text], async = True)
Dadurch wird der Twitter-Stream getrennt und anschließend mit dem neuen Filter verbunden, track_text
, Das ist, was immer aus dem Formular eingereicht wurde. Eine wichtige Änderung gegenüber dem vorherigen Tutorial besteht darin, dass der Stream des Twitter-Streams im asynchronen Modus ausgeführt wird, async = True
. Dadurch wird die Streamverbindung in einem Hintergrundthread ausgeführt, sodass der Webserver als Hauptthread ausgeführt wird.
Fügen Sie am Ende ein paar Zeilen hinzu, um den Stream im asynchronen Modus zu starten und anschließend den Webserver zu starten:
stream.filter (track = ['jeremy'], async = True) application.listen (8888) tornado.ioloop.IOLoop.instance (). start ()
Damit startet der Webserver den Port 8888. Richten Sie einen Webbrowser auf http: // Ihre-raspi-ipaddress: 8888 /, und Sie sehen das Formular:
Tweetbox-WebseitenformularGeben Sie etwas anderes ein, das Sie verfolgen möchten Himbeere und klicken Sie auf "Senden". Nach fünf Sekunden werden stattdessen diese Tweets verfolgt.
In dieser Phase füge ich die Farbzuordnung zur Konfiguration hinzu, damit Sie die Wörter einstellen können, durch die die RGB-Hintergrundbeleuchtung geändert wird. Da es sieben Einstellungen gibt, möchte ich diese nicht wirklich jedes Mal erneut eingeben, wenn ich die Anwendung starte. Ich speichere sie also in einer Datei.
Das Programm verwendet Pickle zum Speichern und Laden der Konfigurationsdatei. Außerdem gibt es eine Initialdatei Datei existiert Aktivieren Sie diese Option, und fügen Sie diese Importe oben hinzu:
Import Pickle aus Genericpath Import existiert
Das DisplayLoop
Die Klasse ist bereits für die Verwaltung der backlight_map
Also werde ich das erweitern, damit es sich um das kümmert, was ich gerade verfolge, track_text
. Read- und Write-Konfigurationsmethoden werden hier ebenfalls hinzugefügt:
class DisplayLoop (StreamListener): PICKLE_FILE = '/home/pi/py/tweetbox.pkl' def __init __ (self): self.lcd = Adafruit_CharLCDPlate () self.lcd.backlight (self.lcd.RED) self.lcd.clear () self.track_text = 'jeremy' self.backlight_map = 'red': self.lcd.RED, 'green': self.lcd.GREEN, 'blue': self.lcd.BLUE, 'yellow': self. lcd.YELLOW, 'teal': self.lcd.TEAL, 'violet': self.lcd.VIOLET self.msglist = [] self.pos = 0 self.tweet = 'Noch nichts' def write_config (self): data = "track_text": self.track_text, "backlight_map": self.backlight_map output = open (self.PICKLE_FILE, 'wb') pickle.dump (data, output) output.close () def read_config (self): if existiert (self.PICKLE_FILE): pkl_file = open (self.PICKLE_FILE, 'rb') data = pickle.load (pkl_file) pkl_file.close () self.track_text = data ["track_text"] self.backlight_map = data ["backlight_map "]
Ändern Sie die Anforderungshandler, um die Farben zu berücksichtigen. Weiter unten sehen Sie, dass ich beim Rendern der Hauptseite die aktuelle Konfiguration übergeben werde. Das heißt, ich kann das Konfigurationsformular mit den aktuellen Einstellungen ausfüllen, wenn es angefordert wird.
Klasse MainHandler (tornado.web.RequestHandler): def get (self): inverted_map = v: k für k, v in display_loop_instance.backlight_map.items () self.render ("templates / form3.html"), config_track = display_loop_instance .................................... von........ ................................................................... ......... ....... ....... ..... ....... ..... ..... ....... ........................................................ inverted_map [Adafruit_CharLCDPlate.VIOLET]) Klasse ConfigHandler (tornado.web.RequestHandler): def post (selbst): config_track = self.get_argument ("config_track") colour_map = self.get_argument ("config_red"): .ack_a.a.r. (.) (.). ._a_a_d.jpg ("config_green"): Adafruit_CharLCDPlate.GREEN, self.get_argument ("config_blue"): Adafruit_CharLCDPlate.BLUE self.get_ Argument ("config_violet"): Adafruit_CharLCDPlate.VIOLET set_config (config_track, colour_map) self.write ("Jetzt% s verfolgen"% config_track)
Eine Technik, die Sie hier beachten sollten, ist, wie ich die Farbkarte invertiere. Bei der Bearbeitung möchte ich, dass die Karte ist Wort> Farbe aber wenn ich es in der Form konfiguriere, will ich Farbe> Wort. Ein Python-Wörterbuchverständnis kann verwendet werden, um die Karte in einer einzigen Anweisung umzukehren: v: k für k, v in display_loop_instance.backlight_map.items ()
Zur Unterstützung der Farbeinstellungen ist ein neues HTML-Formular erforderlich. Ich muss auch die Formulareingabefelder mit den aktuellen Einstellungen füllen, indem ich Tornados Schablonensystem verwende. Das ist sehr grundlegend, ich nehme nur die Werte, die in die übernommen wurden machen
Funktion und ziehen sie beispielsweise hier in der Vorlage heraus config_track
.
Himbeer-Pi-Tweetbox
Nun, da ich die Konfiguration speichern und laden kann, erstere Neustart
Routine muss etwas anspruchsvoller sein:
def set.config (track_text, colour_map): display_loop_instance.set_text ("Aktualisieren der Konfiguration") stream.disconnect () display_loop_instance.track_text = track_text display_loop_instance.backlight_map = colorour_map display_loop_instance.write_configay () disconnect… stream.filter (track = [display_loop_instance.track_text], async = True) display_loop_instance.set_text ("Aktualisierte Konfiguration")
Da es mehr als ein Neustart ist, heißt es jetzt set_config
. Hier rufe ich jetzt an write_config
um die Änderungen in der Datei zu speichern.
Alles, was übrig bleibt, sind einige Änderungen, die in der Konfiguration beim Start zu lesen sind:
display_loop_instance = DisplayLoop () display_loop_instance.read_config ()
Und um den Stream von dieser Einstellung aus zu starten, anstatt "jeremy"
:
stream = Stream (auth, display_loop_instance) stream.filter (track = [display_loop_instance.track_text], async = True)
Starten Sie das Programm, rufen Sie einen Webbrowser auf http: // your-raspi-ipaddress: 8888 / auf, und Sie sehen das Formular:
Web-FormularEs gibt ein paar Dinge, die bei diesem Programm nicht besonders schlau sind:
Die Verzögerung, während sich die Konfiguration ändert, ist auf die asynchrone Natur des Programms zurückzuführen. Es gibt einen Thread, der den Twitter-Stream verwaltet, einen Thread, der die Anzeige durchläuft, und den Haupt-Thread, der den Webserver ausführt.
Wenn ich die Einstellungen im Stream ändern möchte, muss ich die Verbindung trennen und dann die neuen Optionen wieder herstellen. Leider gibt es kein Ereignis von tweepy, das mir mitteilt, wann ich die Verbindung erfolgreich getrennt habe. Bis jetzt habe ich mich gerade fünf Sekunden zwischen dem Trennen und dem Wiederherstellen der Verbindung verzögert.
Um diese Verzögerung zu beseitigen, starte ich eine neue Verbindung, während die alte Verbindung getrennt wird, sodass ich nicht warten muss. Natürlich bedeutet dies, dass es an einem Punkt zwei Streams geben kann, die Tweets empfangen. Dies wäre auf dem Display verwirrend, da Sie das alte Tracking und das neue Tracking zusammen sehen würden.
Deshalb verbinde ich den alten Stream kurz vor dem Trennen mit einem Listener, der mit den ankommenden Tweets nichts tut. Hier ist die Definition des NullListener
und die Änderungen an der set_config
Routine:
Klasse NullListener (StreamListener): def on_data (self, data): def set_config übergeben (track_text, colour_map): print "restarting" display_loop_instance.set_text ("Konfiguration wird aktualisiert") #Kill the old stream asynchron. global stream stream.listener = NullListener = ) stream.disconnect () display_loop_instance.track_text = track_text display_loop_instance.backlight_map = colour_map display_loop_instance.write_config () #Make einen neuen Stream stream = Stream (auth, display_loop_instance) stream.filter (track = [display_loop_instance) display_loop_instance.set_text ("Aktualisierte Konfiguration")
Hinsichtlich der Jetzt verfolgen… Antwort wird die aktuelle Version des Formulars an die übermittelt ConfigHandler
was die Einstellungen ändert und diese hässliche Antwort zurückgibt. Ich möchte wirklich, dass das Formular mit den neuen Einstellungen wieder angezeigt wird.
Ich kann dies erreichen, indem ich den Benutzer zurück auf die /
URL. Es gibt auch keine Notwendigkeit für die ConfigHandler
trotzdem kann ich das definieren erhalten
und Post
Methoden auf der MainHandler
und senden Sie das Formular einfach dort ab:
Klasse MainHandler (tornado.web.RequestHandler): def get (self): inverted_map = v: k für k, v in display_loop_instance.backlight_map.items () self.render ("templates / form4.html"), config_track = display_loop_instance .................................... von........ ................................................................... ......... ....... ....... ..... ....... ..... ..... ....... ........................................................ inverted_map [Adafruit_CharLCDPlate.VIOLET]) def post (selbst): config_track = self.get_argument ("config_track") colour_map = self.get_argument ("config_red"): Adafruit_CharLCDPlate.RED, (selbst). GRÜN, self.get_argument ("config_blue"): Adafruit_CharLCDPlate.BLUE, self.get_argument ("config_yellow"): Adafruit_CharLCDPlate.YELLOW, self.get_argument ("config_teal"): Adafruit_CharLCDPlate.TEO (). Adafruit_CharLCDPlate.V IOLET set_config (config_track, colour_map) #Verwenden Sie eine Weiterleitung, um Probleme mit Aktualisierungen im Browser aus einem Formular zu vermeiden. Post. self-redirect ("/") application = tornado.web.Application ([(r "/", MainHandler), ])
Zum Schluss das Styling. Wenn Sie dieses Aussehen wirklich schön machen, könnte dies ein völlig neues Tutorial für sich sein, aber ein wirklich guter Anfang ist die Einführung eines Rahmens, der sich um das Styling kümmert.
Ich bin Bootstrap für das Styling und JQuery für das Scripting. Beide sind auf CDNs verfügbar, sodass Sie nichts herunterladen müssen. Fügen Sie sie einfach in den Kopfbereich der Seite ein:
Himbeer-Pi-Tweetbox
Damit das Formular besser aussieht, verwenden wir den horizontalen Formularstil von Bootstrap:
Um die Benutzeroberfläche etwas aufzufrischen, zeigen wir dem Benutzer an, dass der Raspberry Pi aktualisiert wird, wenn er auf die Schaltfläche klickt einreichen Taste. Dies erfordert ein wenig Javascript, um das Formularübermittlungsereignis zu erfassen. Ändern Sie den Schaltflächentext in Aktualisierung… und deaktivieren Sie die Schaltfläche:
Und hier ist die fertige Web-Benutzeroberfläche:
Das fertige WebformularDieses Tutorial wurde gegenüber dem vorherigen erweitert, um der RGB-LCD-Tweet-Box eine Web-Benutzeroberfläche hinzuzufügen. Sie können jetzt steuern, was Sie auf dem Bildschirm verfolgen, und die gewünschten Farben für die Hintergrundbeleuchtung von Ihrem Telefon, Tablet oder Desktop-Computer aus.