Deze zelfstudie verlengt de vorige zelfstudie, Hoe een Tweet controlled RGB LCD te bouwen, door het toevoegen van webpaginabesturing. Hiermee kunt u de configuratie van de Tweetbox on the fly wijzigen vanaf uw laptop, tablet of telefoon.
In deze tutorial leer je nuttige technieken, zodat je de controle over de webpagina's kunt toevoegen aan je eigen Raspberry Pi-projecten.
sudo pip installeer tornado
Alle code voor dit project is hier beschikbaar:
https://github.com/jerbly/tutorials/tree/master/tweetbox
Om samen te vatten, had je tegen het einde van de vorige tutorial een Raspberry Pi met een RGB LCD aangesloten op Twitter die openbare tweets kreeg die overeenkomen met een filter. De RGB-achtergrondverlichting zou van kleur veranderen afhankelijk van andere overeenkomende woorden in de tweet. Om het filter en de kleurkaart te configureren, moest u de code wijzigen en het programma opnieuw starten.
In deze eerste fase voeg ik een webserver toe aan het programma. Vervolgens kunt u naar een webpagina gaan en de configuratie live bijwerken. Ik zal Tornado gebruiken, er zijn veel verschillende web frameworks voor Python en ik heb er veel van gebruikt in de loop van de jaren maar dit is nu mijn ga naar kader. Het tikt veel vakken voor veel verschillende projecten.
Bekijk de Hallo Wereld bijvoorbeeld op de Tornado-pagina, zodat u kunt zien hoe u een handler opzet en een tornado-serverthread start. Ik zal precies hetzelfde principe hier gebruiken.
Ik neem de tweetbox.py
code uit de vorige zelfstudie en voeg het webraamwerk toe. Om te beginnen heb ik een eenvoudig formulier waarmee we kunnen wijzigen wat ik volg op Twitter. Voeg eerst enkele imports toe aan de bovenkant van het script:
import tornado.ioloop import tornado.web
Vervolgens heb ik twee handlers nodig: een om het HTML-formulier te tonen en een tweede om de formulierinzending te ontvangen:
class MainHandler (tornado.web.RequestHandler): def get (self): self.render ("templates / form1.html") class ConfigHandler (tornado.web.RequestHandler): def post (self): config_track = self.get_argument ( "config_track") restart (config_track) self.write ("Nu volgen% s"% config_track) application = tornado.web.Application ([(r "/", MainHandler), (r "/ config", ConfigHandler),] )
Let daar op MainHandler
toepassingen krijgen
en ConfigHandler
toepassingen post
, het HTML-formulier gebruikt de postmethode om de formuliergegevens terug te sturen naar de webserver. Om de vorm te dienen, de MainHandler
roept eenvoudig de renderfunctie om een sjabloon te laten weergeven en naar de browser te verzenden. Meer hierover in een moment.
Wanneer de formuliergegevens terugkomen ConfigHandler
haalt één argument uit, config_track
. Dit wordt verzonden naar a herstarten
functie om de instellingen te wijzigen met tweepy voordat een eenvoudige tekenreeks wordt weergegeven die aangeeft wat wordt bijgehouden.
Maak de sjabloon door een opgeroepen map toe te voegen templates naar de brondirectory en maak de form1.html
bestand daar:
Raspberry Pi Tweetbox
Dit is een heel eenvoudige vorm met een tekstinvoervak en een verzendknop. Het is alles wat nodig is in dit stadium. Maak vervolgens de herstarten
functie:
def herstart (track_text): stream.disconnect () time.sleep (5) #Altijd tijd voor het loskoppelen van de thread ... stream.filter (track = [track_text], async = True)
Hiermee wordt de Twitter-stream verbroken en vervolgens opnieuw verbonden met het nieuwe filter, track_text
, wat is wat is ingediend vanuit het formulier. Een belangrijke verandering hier, uit de vorige tutorial, is dat de thread Twitter stream in asynchrone modus draait, async = True
. Hiermee wordt de stream-verbinding in een achtergrondthread uitgevoerd, zodat de webserver als hoofdthread wordt uitgevoerd.
Voeg een paar regels toe aan het einde om de stream in de asynchrone modus te starten en vervolgens de webserver te starten:
stream.filter (track = ['jeremy'], async = True) application.listen (8888) tornado.ioloop.IOLoop.instance (). start ()
Hiermee begint de webserver te luisteren op poort 8888. Richt een webbrowser op http: // your-raspi-ipaddress: 8888 / en u ziet het formulier:
Webformulier voor TweetboxVoer iets anders in om te volgen framboos en klik op verzenden. Na vijf seconden zal het overschakelen naar het volgen van deze tweets.
In deze fase voeg ik de kleurkaart toe aan de configuratie, zodat u de woorden kunt instellen die de RGB-achtergrondverlichting activeren om te wijzigen. Omdat er zeven instellingen zijn, wil ik deze niet elke keer opnieuw invoeren als ik de applicatie start, dus ik sla deze op in een bestand.
Het programma gebruikt augurk om het configuratiebestand op te slaan en te laden, plus een initiaal Bestand bestaat controleer dus voeg deze invoer toe aan de top:
importeren inleg van genericpath-import bestaat
De DisplayLoop
klasse is al verantwoordelijk voor het beheer van de backlight_map
dus ik zal dit uitbreiden zodat het zorgt voor wat ik momenteel aan het volgen ben, track_text
. Lees- en schrijfconfiguratiemethoden zijn hier ook toegevoegd:
klasse 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 = 'Nog niets' 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 bestaat (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 "]
Wijzig de verzoekbehandelaars om voor de kleuren te zorgen. Ook hieronder ziet u dat wanneer ik de hoofdpagina render die ik in de huidige configuratie passeer. Dit betekent dat ik het configuratieformulier met de huidige instellingen kan vullen wanneer daarom wordt gevraagd.
class MainHandler (tornado.web.RequestHandler): def get (self): inverted_map = v: k voor k, v in display_loop_instance.backlight_map.items () self.render ("templates / form3.html", config_track = display_loop_instance .track_text, config_red = inverted_map [Adafruit_CharLCDPlate.RED], config_green = inverted_map [Adafruit_CharLCDPlate.GREEN], config_blue = inverted_map [Adafruit_CharLCDPlate.BLUE], config_yellow = inverted_map [Adafruit_CharLCDPlate.YELLOW], config_teal = inverted_map [Adafruit_CharLCDPlate.TEAL], config_violet = inverted_map [Adafruit_CharLCDPlate.VIOLET]) class ConfigHandler (tornado.web.RequestHandler): def post (self): config_track = self.get_argument ("config_track") colour_map = self.get_argument ("config_red"): Adafruit_CharLCDPlate.RED, self .get_argument ("config_green"): Adafruit_CharLCDPlate.GREEN, self.get_argument ("config_blue"): Adafruit_CharLCDPlate.BLUE, self.get_argument ("config_yellow"): Adafruit_CharLCDPlate.YELLOW, self.get_argument ("config_teal"): Adafruit_CharLCDPlate.TEAL , self.get_ argument ("config_violet"): Adafruit_CharLCDPlate.VIOLET set_config (config_track, color_map) self.write ("Nu volgt% s"% config_track)
Een techniek om op te merken is hoe ik de kleurenkaart omkeer. Bij het verwerken wil ik de kaart zijn woord> kleur maar bij het configureren van het in de vorm die ik wil kleur> woord. Een woordenboekbegrip in Python kan worden gebruikt om de kaart in één instructie om te keren: v: k voor k, v in display_loop_instance.backlight_map.items ()
Een nieuw HTML-formulier is vereist om de kleurinstellingen te ondersteunen. Ook moet ik de formulierinvoervakken vullen met de huidige instellingen door gebruik te maken van het sjabloonsysteem van Tornado. Dit is heel eenvoudig, ik neem gewoon de waarden over die zijn doorgegeven aan de geven
functie en trek ze hier uit in de sjabloon bijvoorbeeld config_track
.
Raspberry Pi Tweetbox
Nu kan ik de configuratie opslaan en laden, de eerste herstarten
routine moet iets geavanceerder zijn:
def set_config (track_text, color_map): display_loop_instance.set_text ("Configuratie bijwerken") stream.disconnect () display_loop_instance.track_text = track_text display_loop_instance.backlight_map = colour_map display_loop_instance.write_config () time.sleep (5) #Altijd tijd voor de thread om disconnect ... stream.filter (track = [display_loop_instance.track_text], async = True) display_loop_instance.set_text ("Bijgewerkte configuratie")
Omdat het meer is dan een herstart is de naam nu SET_CONFIG
. Het is hier waar ik nu bel write_config
om de wijzigingen in het bestand op te slaan.
Het enige dat overblijft is een paar wijzigingen om te lezen in de configuratie bij het opstarten:
display_loop_instance = DisplayLoop () display_loop_instance.read_config ()
En om de stream vanuit deze instelling te starten in plaats van 'Jeremy'
:
stream = Stream (auth, display_loop_instance) stream.filter (track = [display_loop_instance.track_text], async = True)
Start het programma, richt een webbrowser op http: // your-raspi-ipaddress: 8888 / en je ziet het formulier:
webformulierEr zijn een paar dingen die niet erg gelikt zijn over dit programma:
De vertraging tijdens het wijzigen van de configuratie is te wijten aan de asynchrone aard van het programma. Er is een thread die de Twitter-stream beheert, een rode draad door het display en de rode draad van de webserver.
Wanneer ik de instellingen op de stream wil wijzigen, moet ik deze loskoppelen en vervolgens opnieuw verbinden met de nieuwe opties. Helaas is er geen evenement van tweepy om me te vertellen wanneer ik met succes heb losgekoppeld en zo heb ik tot nu toe slechts vijf seconden vertraagd tussen verbroken en opnieuw verbonden.
Om deze vertraging te verwijderen, start ik een nieuwe verbinding terwijl de oude verbinding verbreekt, dus ik hoef niet te wachten. Dit betekent natuurlijk dat er op een bepaald moment twee streams kunnen zijn die tweets ontvangen. Dit zou verwarrend zijn op het display omdat je de oude tracking en de nieuwe tracking gecombineerd zou zien.
Daarom zal ik vlak voor het loskoppelen de oude stream verbinden met een luisteraar die niets doet met de inkomende tweets. Hier is de definitie van de NullListener
en de wijzigingen in de SET_CONFIG
routine:
class NullListener (StreamListener): def on_data (self, data): pass def set_config (track_text, colour_map): print "restarting" display_loop_instance.set_text ("Update configuratie") #Klik de oude stream asynchroon globaal streamen stream.listener = NullListener ( ) stream.disconnect () display_loop_instance.track_text = track_text display_loop_instance.backlight_map = color_map display_loop_instance.write_config () # Maak een nieuwe stream stream = Stream (auth, display_loop_instance) stream.filter (track = [display_loop_instance.track_text], async = True) display_loop_instance.set_text ("Bijgewerkte configuratie")
Betreffende de Nu volgen ... antwoord, de huidige versie van het formulier wordt verzonden naar de ConfigHandler
die de instellingen wijzigt en deze lelijke reactie retourneert. Wat ik echt wil is dat het formulier opnieuw verschijnt met de nieuwe instellingen op zijn plaats.
Ik kan dit bereiken door de gebruiker terug te leiden naar de /
URL. Ook is er echt geen behoefte aan de ConfigHandler
hoe dan ook, ik kan het definiëren krijgen
en post
methoden op de MainHandler
en simpelweg het formulier daar inzenden:
class MainHandler (tornado.web.RequestHandler): def get (self): inverted_map = v: k voor k, v in display_loop_instance.backlight_map.items () self.render ("templates / form4.html", config_track = display_loop_instance .track_text, config_red = inverted_map [Adafruit_CharLCDPlate.RED], config_green = inverted_map [Adafruit_CharLCDPlate.GREEN], config_blue = inverted_map [Adafruit_CharLCDPlate.BLUE], config_yellow = inverted_map [Adafruit_CharLCDPlate.YELLOW], config_teal = inverted_map [Adafruit_CharLCDPlate.TEAL], config_violet = inverted_map [Adafruit_CharLCDPlate.VIOLET]) def post (self): config_track = self.get_argument ("config_track") colour_map = self.get_argument ("config_red"): Adafruit_CharLCDPlate.RED, self.get_argument ("config_green"): Adafruit_CharLCDPlate. GREEN, self.get_argument ("config_blue"): Adafruit_CharLCDPlate.BLUE, self.get_argument ("config_yellow"): Adafruit_CharLCDPlate.YELLOW, self.get_argument ("config_teal"): Adafruit_CharLCDPlate.TEAL, self.get_argument ("config_violet"): Adafruit_CharLCDPlate.V IOLET set_config (config_track, color_map) #Gebruik een omleiding om problemen met vernieuwingen in de browser van een formulier te voorkomen. Self.redirect ("/") application = tornado.web.Application ([(r "/", MainHandler), ])
Eindelijk, styling. Zien er echt mooi uitzien zou een geheel nieuwe tutorial op zichzelf kunnen zijn, maar een echt goede start is om een framework te introduceren om voor veel styling voor je te zorgen.
Ik ben Bootstrap voor styling en JQuery voor scripting. Beide zijn beschikbaar op CDN's, zodat u niets hoeft te downloaden; neem ze gewoon op in het head-gedeelte van de pagina:
Raspberry Pi Tweetbox
Om het formulier er beter uit te laten zien, gebruiken we de horizontale-formulierstijl van Bootstrap:
Tot slot om een beetje Pools in de gebruikersinterface te plaatsen, geven we de gebruiker aan dat de Raspberry Pi wordt bijgewerkt wanneer ze op de gebruikersinterface klikken. voorleggen knop. Dit omvat een klein beetje Javascript om de gebeurtenis voor het indienen van formulieren vast te leggen, de knoptekst in te veranderen Bijwerken ... en schakel de knop uit:
En hier is de voltooide webinterface:
Het voltooide webformulierDeze tutorial is uitgebreid met de vorige om een web-gebruikersinterface toe te voegen aan de RGB LCD Tweet Box. U kunt nu bepalen wat u op het scherm en de achtergrondverlichtingskleuren wilt zien die u wilt vanaf uw telefoon, tablet of desktopcomputer.