raspberry pi: full stack: a whirlwind tour of full-stack ...prophet/raspberrypi/raspberry... ·...

136

Upload: others

Post on 09-Oct-2020

12 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section
Page 2: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section
Page 3: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section
Page 4: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

RaspberryPi:FullStackAwhirlwindtouroffull-stackwebapplication

developmentontheRaspberryPi

PeterDalmaris,PhD

PublishedbyTechExplorations

Copyright©PeterDalmaris,2015

EbookFormattingbyGuidoHenkel

Allrightsreserved.ExceptaspermittedundertheU.S.CopyrightActof1976,nopartofthispublicationmaybereproduced,distributedortransmittedinanyformorbyanymeans,orstoredinadatabaseorretrievalsystem,without

thepriorwrittenpermissionofthepublisher.

Page 5: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

AboutthisbookThisbookisaproject.Usingastep-by-stepapproach,itwillhelpyouexploreyourRaspberryPiinawaythatwillhelpyouappreciatebothitshardwareanditssoftwarecapabilities.

Inwritingthisbook,Ihavetriedtobreakawayfromtheclassictextbookformatofchapters,sectionsandlongparagraphs,andinsteadpresentthishighlytechnicaltopicasanengineerwouldapproachit:withalotofiterations,eachdeliveringagradualimprovementtotheoverallfunctionalityofthesystem,witheachiterationcomprisingofseveralstep.

Eachstepismarkedwithanumbersothatyoucanrefertoitinyourownnotes,orinyourcommunicationwithmeorotherreadersofthisbook.

AllofthecodethatIdescribeinthisprojectishostedonGithub,fromwhereyoucandownloaditonyourcomputer.ItismuchbettertocopycodefromtheGithubrepositoryratherthantryingtomanuallycopyitfromthisbook.IprovidelinkstothefilethatIamdiscussingatdifferentpartsoftheproject;simplyclicktoalinkandcopythecodefromthescreen.

Inwritingthisproject,Imakeafewassumptionsaboutyou:

1. Youcanprogramacomputerinatleastoneprogramminglanguage,notnecessarilyPython

2. Youarenotafraidoflearninganewprogramminglanguage3. Youarecomfortableworkingwithelectronics.Youwillneedsomebasicbeginner-

levelskills.4. Youarenoteasilyfrustrated.Whatyouareabouttodointhisprojectrequires

patience!5. Youliketoexploredifferenttechnologies.Themodernmakermustbegoodin

multipletechnologies,hardware,softwareanddifferent“sub-categories”ofeachone.

ThegoalistoshowyouhowtosetupaRaspberryPicomputersothat:

Itmeasurestemperatureandhumidity.Itreportsthevaluesinrealtimeviaawebbrowser.Itrecordthesevaluesinadatabase.Itretrievestheserecordsanddisplaysthemintabularformatandingraphicalformatinawebbrowser.Itsendsthevaluestoagraphicalanalysiscloudservice.

Youwilllearn:

HowtosetuptheminimalRaspbianoperatingsystemtotheRPi.InstalltheaPythonvirtualenvironmentInstallanduseFlask,aPython-basedwebmicro-frameworkInstallanduseuWSGIastheapplicationserverforFlaskInstallanduseNginxlight-weightwebserver

Page 6: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

UsetheRPiGPIOsasdigitalinputandoutputsUseaDHT22humidityandtemperaturesensorInstallandusetheSQLitedatabaseUsetheGoogleChartAPItocreatevisualrepresentationsofthesensordataUseJQuerytoaddinteractivitytowebpagesUsePlotlyforgraphicalanalysisofsensordata

Page 7: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

VideocoursecompanionAdetailedvideo-basedversionofthisprojectisalsoavailable.Itcontainstheexactsameinstructionsasthisbook,butinfullHDvideo.Youcanseetheprojectcomingtolife.Hardwarewiring,blinkingLEDs,sensors,codewalkthroughs,everythinghappensonscreen.

Thisvideocoursepresentsthisprojectin57lecturesand7hoursofcontent.Seebelowtheimageforalistofsectionsandlectures.

Iinviteyoutowatchafewsamplevideolectures.Ifyouwishtopurchasethisvideocourse,youcantakeadvantageofthespeciale-bookreaderpricingof$9byusingcouponcodeebookatcheckout,orclickonthislink.

Videocourse,listofsectionsandlecturesSection:1-Introductiontothecourse

Lecture1:Introduction02:17Lecture2:AbouttheRaspberryPi04:45Lecture3:Components04:22Lecture4:DetailedListofComponentsyouwillneedLecture5:Pleasereadthisbeforecontinuing!

Section:2-TheOperatingSystemLecture6:SectionIntro00:25Lecture7:InstallingminiRaspbianusingMacOSX16:35Lecture8:InstallingminiRaspbianusingWindows14:42Quiz1:SectionQuiz3questionsLecture9:SectionConclusion00:14

Page 8: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Section:3-PythonandGPIOsLecture10:SectionIntro00:25Lecture11:SetupPython13:55Lecture12:GPIObasics08:36Lecture13:MakeanLEDblink17:57Lecture14:Readthestatusofabutton09:52Lecture15:Readtemperatureandhumidityfromadigitalsensor15:53Quiz2:SectionQuiz4questionsLecture16:SectionConclusion00:21

Section:4-SetuptheWebapplicationstackLecture17:SectionIntro00:24Lecture18:WhatistheWebapplicationstack?07:03Lecture19:InstallNginx,theWebserver02:49Lecture20:InstallFlaskandvenv09:17Lecture21:InstalluWSGI18:40Lecture22:SetupUpstart10:19Lecture23:Aboutlogfiles08:49Lecture24:ServingstaticassetsandSkeleton11:55Lecture25:StylingourWebapplicationwithSkeleton09:12Lecture26:DebuggingaFlaskapplication05:46Quiz3:SectionQuiz6questionsLecture27:SectionConclusion00:19

Section:5-BuildingasimpleFlaskapplicationontheRaspberryPiLecture28:SectionIntro00:31Lecture29:ShowDHT22sensordatainthebrowser14:07Lecture30:InstalltheSQLite3database14:05Lecture31:UseaPythonscripttostoresensorreadingtothedatabase07:34Lecture32:AutomatesensordataloggingwithcronandSQLite312:57Lecture33:Showhistoricalsensordatainthebrowser12:48Quiz4:SectionQuiz3questionsLecture34:SectionConclusion00:22

Section:6-Improvingourapplicationwithdate-timerangerecordselectorLecture35:SectionIntro00:26Lecture36:Selectinghistoricalsensordatarecordswithatime-daterange07:12Lecture37:Defineadate-timerangeintheURL10:49Lecture38:TimezonesinRasbian02:03Lecture39:Validatingtimestamps06:24Lecture40:Tidyingup:refactorourapplicationcode03:32Quiz5:SectionQuiz3questionsLecture41:SectionConclusion00:31

Section:7-ImprovingtheuserinterfaceLecture42:SectionIntro00:29Lecture43:Addingdaterangeradiobuttons19:17Lecture44:VisualisesensordatawithGoogleCharts18:35Lecture45:Installadatetimepickerwidgets07:11Lecture46:Settingupthedatetimepickerwidget07:57Lecture47:Settinguptimezonesontheclientside08:31Lecture48:Settinguptimezonesontheserverside14:00Lecture49:Linkthetwopagesoftheapplication05:29Quiz6:SectionQuiz3questionsLecture50:SectionConclusion00:28

Page 9: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Section:8-SetupcloudchartingandanalysiswithPlotlyLecture51:SectionIntro00:15Lecture52:SetupPlotly10:19Lecture53:AddPlotlylinks10:05Lecture54:AddPlotlysupporttotheFlaskapplicationscript11:11Quiz7:SectionQuiz3questionsLecture55:SectionConclusion00:28

Section:9-OtherusefulthingstoknowLecture56:InstallandconfigureaWifiUSBdongleforwirelessnetworking22:22

Section:10-ConclusionLecture57:Conclusion

Page 10: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

DiscussionforumandemaillistThisbookhasawebpage!Inthatpage,Iampostingupdatesanderrata,aswellashostthebook’sdiscussionforum.

Ifyouwishtobenotifiedautomaticallyofupdatesandcorrections,pleaseconsidersigninguptothebook’semaillistathttp://txplore.com/rpi-full-stack-book/.

Herearesomemoredetailsaboutthebook’sdiscussionforum.Infact,therearetwodiscussionforums:

1. Ifyouhavepurchasedaccesstothevideocourseversionofthisbookattxplore.tv(describedearlier),thereisadiscussionforumforeachlecture.Youcanusethesededicatedforumstoaskquestionsordiscussthespecifictopicofalecture.

2. Ifyouhavenotpurchasedthevideocourseversionofthisbook,youcanusethegeneraldiscussionforumathttp://txplore.com/rpi-full-stack-book/.

Page 11: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

TomakethemostofthisbookAllcodediscussedinthisbookisavailableonGithubathttps://github.com/futureshocked/RaspberryPi-FullStack/.Linksthattakeyoudirectlytoindividualfilesfromwhereyoucancopythiscodearegiventhroughoutthetext.Youcanalsodownloadtheentirerepositorywithasingleclicksothatyoucanhavethiscodeonyourcomputer.

Tocompletetheproject,youwillneedthesematerials:ARaspberryPi,anyversionAWindows,MacorLinuxcomputerADHT11orDHT22sensorAn5mmLEDResistorsAbreadboardandjumperwiresAccesstotheInternetAUSBWifidongle,ifavailable

Purchasethepartsfromthevendorofyourchoice.Youcanalsopurchaseabundlewith

Page 12: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

everythingyouneedfrommyAmazonaffiliateshop,athttp://txplore.com/raspberry-pi-full-stack-parts-bundle/.

IfyouarenotintheUSA,youcanuseaforwardingservicelikeShipitotohaveyourbundlecomponentsshippedanywhereintheworld.

Page 13: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERONEIntroduction

TheRaspberryPiisalowcostcomputer,popularwithpeoplewhowantdirectaccesstoitshardware.Ithasrevolutionisedcomputereducationbycombininglowpricewithaccessibility.

TheRaspberryPiModelA

Thestudent,forthefirsttimesincetheearlydaysofthePCrevolution,hasdirectphysicalaccesstothehardware.TheRaspberryPiismadeforlearning.Tomakethemostofit,youmustspendtimetogainanunderstandingofthebasicsofitshardware,itsoperatingsystem,programming,andtheperipheralsthatyoucanconnecttoit.

Thisprojectisdesignedforthatpurpose.It’sobjectiveistotakeyoutoawhirlwindtouroftheRaspberryPi,andintroduceyoutoeverythingthatisgreataboutit.

Wewillstartwithinstallingandconfiguringtheoperatingsystem,thehardway.Nopointandclick,nographicaluserinterface.Justakeyboardandacommandlineonthescreenwillseparateyoufromthecomputer.

Then,wewilllookintotheRaspberryPi’sGeneralPurposeInput/OutputpinsandinteractwithLEDsandswitchesusingPython.PythonisoneofthemanyprogramminglanguagesyoucanuseontheRPi.

We’llmoveontolearnhowtoconnectanenvironmentsensor,andaddtoourPython

Page 14: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

skillstheabilitytoinstallthird-partylibraries.

Next,we’llsetupawebserversothatwecanaccessoursensordataviaawebbrowser.Thiswillexposeyoutoavarietyofwebdevelopmentskills,likethewebapplicationdevelopmentframework,Flask,theuWSGIapplicationserver,asimplesingleuserdatabase,SQLite,andJavascript/JQueryforwebclient-sideprogramming.

Youwillalsolearnhowtotakeadvantageofcloudcomputingresources,andinparticularyouwilluseGoogleChartstocreatechartsofthesensordata,andPlotlyformoreadvancedprocessing.

Let’sstart!

Page 15: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERTWOAbouttheRaspberryPi

TheRaspberryPi,atthetimeIamcreatingthisproject,isavailableinseveraldifferentmodels.

ModelA,ModelA+,ModelBandModelB+.InthisprojectIamusingtheModelB.

SomeoftheavailableRaspberryPimodels

AllRaspberryPi’ssharesomecommonfeatures.Lookingatthecircuitboard,youcansee:

1. TheProcessorandRAMchip2. TheLANcontrollerchip3. AHDMIvideooutputconnector4. Acompositeanalogvideoconnector(onmodelsAandB)5. AnSDcardconnector6. Amicro-USBpowerconnector7. AUSBport8. AnEthernetport(onmodelBs)9. Acameraconnector

Page 16: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

10. AndtheveryimportantGPIOheaders

TheRaspberryPi,commoncomponents

TheRaspberryPicomesasasinglePCB.Nokeyboardandmouse,noscreen,notevenapowersupply.Youhavetoprovideallthat.Inthisproject,wewillbeusingtheRaspberryPiinsocalled“headless”mode.Thismeansthatwewillnotbeconnectingittoakeyboardormouse.Instead,wewillworkwiththeRaspberryPiviaanSSHnetworkconnection.Don’tworryifyoudon’tquiteunderstandwhatthismeans,Iwillshowyoueverythingyouneedtoknow,stepbystep.

Asacomputer,andunlikemicro-controllersliketheArduino,theRaspberryPineedsanoperatingsystem.Thereareseveraloptionstochoosefrom:

Rasbian,theRaspberryPiFoundation’spreferredoperatingsystemdistributionUbuntu,OpenelecOSMCPidoraRISCOSAndMinibian,mypreferreddistribution.

AllofthemexceptforRISCOSareflavoursofLinux.

MinibianisaminimalistversionofRaspbian.Itkeepseverythingthatisimportantandthrowsawaythegraphicaluserinterfaceandafewotherthingsthatarenotreallyneededforourpurposes.Inreturn,wegetasmalldiskfootprintsowecanuseevensmall4GByteSDCards.

Althoughthereisandistributiondesignedforabsolutebeginners,NOOBS,IwillshowyouhowtoinstallMinibianinthenextsection.

Page 17: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

TheRaspberryPicomeswith512MBytesor1GByteofRAM,dependingonthemodel.Videomemoryissharedwithgeneralpurposememory.Inourproject,wewillnotbeusinganyvideooutput,sowewillconfigureourPitonotuseanyvideomemory.

AlthoughthisamountofRAMmayseemtoolittleatatimewhencomputerscomewithmultipleofgigabytes,foranembeddedcomputeritismorethanenough.PeoplerunmultiplayergameserverslikeMinecraftonit,andsmallproductionwebserversanddatabaseserver.OthershaveevenusedtheRPiasanodeforsmallsupercomputers.Withabitofplanning,theRaspberryPicandoamazingthings.

RaspberryPirunningaMinecraftserver

TomakesomethingusefulwiththeRaspberryPi,justlikewithanycomputer,youneedapplicationsoftware.Youcaneitherdownloadreadymadesoftware,orwriteyourown.Inthisproject,Iwillshowyouboth.Youwilldownload,installandconfigurevarioustypesofservers,andyouwillwriteyourownapplicationinPython.

YouwillnotbecomeanexpertPythonprogrammer,butyouwillbecomefamiliarwithitenoughtobebothusefulanddangerous.That’sagreatstart!

Finally,theRaspberryPirarelyworksinisolation.IthasafastEthernetcommunicationssocketthroughwhichyoucanconnectittotheInternet.YoucanalsoattachedaWifiUSBmoduleandgowireless.WewilltakeadvantageofthiscapabilityandmakeitpossibleforourapplicationtointeractwithInternetbasedwebservices.Youwillalsobeabletoaccessyourapplicationviaawebbrowser,potentiallymakingitpossibletoaccessyourRaspberryPifromanywhereintheworld.

Ok,enoughwiththisgeneralintroductiontotheRaspberryPi.Inthenextlecture,Iwilltalkaboutthecomponentsthatyouwillneedforthisproject.

Page 18: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERTHREEInstallanOperatingSystem

WewilluseMiniRasbian,availableatminibianpi.wordpress.com

ThedecisiontouseMiniRaspbianinsteadofRasbianispurelypractical.Inthisproject,weonlyneedsomeofthemanyfeaturesandcapabilitiesofthefullRasbiandistribution.MiniRaspbianisaminimalversionofRasbian.WithMiniRasbian,wegetafullyRaspberryPicompatibleoperatingsystemwithaverysmalldiskfootprintandwithoutanyfeaturesthatarenotstrictlynecessaryforourpurposes.

WecaninstallitonaverysmallSDcard,4GBytesareenough.AndonceitisonthissmallSDcard,wecanmouldittosupporttheexactcapabilitiesweneed.

Let’sgetstarted!

Step1.1DownloadtheOSimagefromminibianpi.wordpress.com.Inthisproject,wewillbeusingthe4threleaseofMinibian

Step1.2OnaMac,useApplePi-Baker(www.tweaking4all.com/news/applepi-baker-v1-6-update)toinstalltheOSimageonanSDcard.

Step1.3Oncethetoolcompletestheprocess,ejecttheSDcardandinsertitintotheRPi.

Step1.4PluginanEthernetcabletotheRPiandturniton.

Step1.5PluginanEthernetcabletotheRPiandturniton.

Step1.6Usingaterminalwindow,likeiTermontheMacorPuttyonWindows,logontoyourRPiforthefirsttime:>[email protected]

Theauthenticityofhost’192.168.111.63(192.168.111.63)’can’tbeestablished.

RSAkeyfingerprintis54:ce:1f:28:34:87:65:48:1f:e6:fd:bb:e0:d9:a8:27.

Areyousureyouwanttocontinueconnecting(yes/no)?

SayYestotheprompt.

Page 19: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step1.7We’llupdatetheinstallation,andtheninstallraspi-configsothatwecanusethisutilitytodosomebasicconfigurationontheRPi:>apt-getupdate#Toupdatethepackagerepositorylists

>apt-getupgrade#ToupgradetheOSwiththelatestfiles

>apt-getinstallraspi-config

Step1.8We’llrunraspi-configtwice.First,toexpandthefilesystemontheSDcardtousealloftheavailablespace.Second,toenablesomeoftheRPi’ssubsystemsthatwillbeusefullater.>raspi-config

Chooseoption1“ExpandFilesystem”.

Whendone,youwillgetaconfirmationscreen:

Page 20: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

HitEntertoclosethiswindow.ThenTabtwicetohighlight“Finish”andEnteragaintocloseraspi-config.Choose“Yes”tomaketheRPitoreboot.

Page 21: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Thiswillstarttheprocessofexpandingthefilesystem.GiveyourRPiaround10minutestofinish(thisdependsonthesizeofyourSDCardandthespeedofyourRPi).

Step1.9Awhilelater,trytologonagainsothatwecanfinishthebasicconfiguration.Remember,thedefaultpasswordis“raspberry”:>[email protected]

Step1.10Useraspi-configagaintochangesomeofthesettings:>raspi-config

First,setupanewpassword:

Page 22: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Youwillseethis,justhitOk:

Page 23: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Ifthisworkedforyou,gotothenextstep.

Atthetimeofwritingthis,thisdidn’tworkforme.Ireceivedthiserror:

Page 24: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Goback,we’llchangethepasswordonthecommandlinelater.Let’smoveonwiththeAdvancedOptions.

Choose“8-AdvancedOptions”

Page 25: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

WewanttoenableSPI,I2CandSerial.Thesearecommunicationschannelsthatwewillfindusefullater.Don’tworryaboutanythingelse,althoughyoucanfeelfreetolookaround.

First,SPI:

Page 26: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Select“Yes”,then“Ok”,“Yes”and“Ok”attheprompts:

Page 27: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Thelast“Ok”willtakeyoubacktothefirstwindowofthetool.Again,select“8-AdvancedOptions”andthen“A7I2C”:

Page 28: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Choose“Yes”,“Ok”,“Yes”,and“Ok”.ThengobacktoAdvancedOptionsandchoose“Serial”:

Page 29: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Select“Yes”toenableSerial.Reboottomakethesechangeseffective:HitTabtwicetohighlightthe“Finish”option,andhitEnter.Hit“Yes”toreboot.

Again,itwilltakearoundaminutefortheRPitoberebooted.

Step1.11Finally,letschangetherootdefaultpassword:>[email protected]

>passwdroot

EnternewUNIXpassword:

RetypenewUNIXpassword:

passwd:passwordupdatedsuccessfully

Chooseagoodpassword.Ifyouforgetit,there’snowaytoresetit.

Page 30: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERFOURSetupPythonandtryouttheGPIOs

AlthoughRaspbiancomeswithPython,wewillnotusethedefaultversion.Instead,wewillusethePythonVirtualEnvironment,orvirtualenv.Withvirtualenv,wecanisolatemultiplePythonenvironments,eachwithitsowninterpreterversionandlibraryset.

Thisisgreatfordoingexperimentswithouthavingtomodifyourmain“production”Pythonenvironment.Ifyoumakeamistake,youcanjustdeleteyourtestvirtualenvironment.Needlesstosay,virtualenvifperfectforexperimenting,whichiswhatwearedoingrightnow.

We’llinstallvirtualenvandtestitbymakinganLEDblink.

Step2.1StartbyinstallingthenecessaryheaderfilesandastaticlibraryforPython.>apt-getinstallpython-dev

Step2.2InstallthePythonvirtualenv>apt-getinstallpython-virtualenv

Step2.3NowwewillcreateanewworkingfolderandactivateanewPythonvirtualenvironmentinit.>cd/var

>mkdirworking

>cdworking

>virtualenvvenv#venvisthenameofthefolderthatwillcontainthevirtualenvironment

Newpythonexecutableinvenv/bin/python

Installingdistribute………….done.

Installingpip……………done.

Page 31: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step2.4Noticethatnowyouhaveanewfolderinthe“working”directory,calledvenv.Inthatfolder,youhaveacompletecopyofthePythonenvironment.

Let’sactivateit:>.venv/bin/activate

(venv)root@raspberrypi:/var/working#

Youexecutethe“activate”scriptinthevenv/bindirectory.Thepromptisupdatedtoincludethe“(venv)”indicator,showingthatyourvirtualenvironmentwiththatnameisactivated.Ifyouhavemultipleenvironments,theonethatisactuallyactivatedwillalwaysbenamedinthecommandlineprompt.

PlayaroundwithPythononthecommandinterpretertoshowthatitworks.

Todeactivate,youcanjustdothis(don’titnowthough!):>deactivate

Step2.5Let’sinstallthePythonRPiGPIOpackageusingthevirtualenvironment’spackagemanager:>pipinstallrpi.gpio

Step2.6We’llmakeanLEDblink.Youwillneedtwomale-femalejumperwires,a5mmredLEDanda~150Ohmresistor.Usethisdiagramtofindoutthepinstouse.Rememberthatpin1isatthebottomoftheboardclosesttothepowerredLED.

TakeawireandconnectthelongpinoftheLEDtopin7(GPIO4).

ConnecttheresistorfromtheshortpinoftheLEDtoawholeinavacantcolumnonthebreadboard.

Takeawireandconnectthefreepinoftheresistortopin6(Ground)ontheRPi.

Page 32: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section
Page 33: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step2.7UsethisprogramtomaketheLEDblink.First,letsinstallatexteditor,thencopytheprogramtotheeditor(don’tworry,lateronwewilluseyourfavouritetexteditoronyourMacorWindowsmachine):>apt-getinstallvim

Whileinyour“working”directory,openupanewvimeditor:>vimblinky.py

Hitthe“i”keytogetviminto“insertmode”,andcopythisprograminthefile:importRPi.GPIOasGPIO##ImportGPIOLibrary

importtime##Import‘time’library(for‘sleep’)

pin=7##We’reworkingwithpin7

GPIO.setmode(GPIO.BOARD)##UseBOARDpinnumbering

GPIO.setup(pin,GPIO.OUT)##Setpin7toOUTPUT

foriinrange(0,20):##Repeat20times

GPIO.output(pin,GPIO.HIGH)##TurnonGPIOpin(HIGH)

time.sleep(1)##Wait1second

GPIO.output(pin,GPIO.LOW)##TurnoffGPIOpin(LOW)

time.sleep(1)##Wait1second

GPIO.cleanup()

InPython,itisveryimportantthatyoukeeptheindentationcorrect.Insidethe“for”loop,thecodeyoubeintentbyexactlyonelevel,eitherspace(s)ortab(s).

Makesuretheprogramwascopiedcorrectly.Thenhittheescapekeytogetvimintocommandmode.Then,saveandquitvimbytyping“:wq”(wforwrite,qforquit).

Nowruntheprogram:>pythonblinky.py

…andwitnessthisbrilliantLEDblinking20times!

Page 34: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step2.8Spendabitoftimelookingattheprogram.NoticehowweimporttheGPIOlibraryatthefirsttime,andthe“time”libraryinthesecondsothatwecanusethesleepfunction.

Then,wesettheboardpinnumberto7.WecanrefertoRPipinintwoways,physicalnumberingorGPIOnumbering.Physicalnumberingaretheactualnumberofeachpinthatwecancount,startingwith1fromthepinthatistheclosesttotheredpowerLED,2theonenexttoit,etc.GPIOnumberingusesthereferencesthattheRPiitselfusesinternally.Asmostofusarehumans,Ithinkweshouldusethephysicalnumberingsystem.

Thatiswhyinline5wesetGPIOmodetoBOARD.

TouseGPIOnumbering,youwouldwrite“GPIO.BCM”.

Next,wetelltheRPithatwewillbeusingthepin7asadigitaloutputpin.

Insidetheloop,weturntheLEDonandofusingtheGPIO.outputmethod.

Whentheloopfinishes,wecleanuptheRPi’sGPIOregisters,andwearedone!YoushouldalwaysfinishyourRPiprogramswiththisinstructionsothatthepinsareleftinaknownandsafestate.

Step2.9Nowlet’sdotheopposite:UseaGPIOpinandaninput,toreadthestateofabutton.

Takeabuttonorswitchandplaceitonyourbreadboard.Abuttonusuallyhas4pins,connectedinpairs.Lookforthepinsthatpointawayfromthebutton,oneithersideofthebutton.Takeajumperwireandconnectoneofthetwotoground.

Onthesecondpin,connectajumperwiretothephysicalpin8ontheRPi,anda10KOhmresistor.Theothersideoftheresistorshouldbeconnectedtopin1ontheRPi,whichprovides3Vofpower.Theresistorcreatesapull-up,sothatwhenthebuttonisnotpressed,itwillconveythe3Vtotheinputpin8.

Thisiswhatyourcircuitshouldlooklikenow:

Page 35: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step2.10JustlikeintheLEDexperiment,openupvimlikethis(downloadbutton.py):>vimbutton.py

Thiswillcreateanewfilewiththefilename“button.py”.Hitthe“i”keytogointotextinsertmode,andcopythistextinit:importRPi.GPIOasGPIO##ImportGPIOLibrary

Page 36: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

inPin=8##Switchconnectedtopin8

GPIO.setmode(GPIO.BOARD)##UseBOARDpinnumbering

GPIO.setup(inPin,GPIO.IN)##Setpin8toINPUT

whileTrue:##Dothisforever

value=GPIO.input(inPin)##Readinputfromswitch

ifvalue:##Ifswitchisreleased

print“NotPressed”

else:##Elseswitchispressed

print“Pressed”

GPIO.cleanup()

Becareful,theindentationsareimportant!

Whenyouhavefinishedcopyingtheprogram,typeEsc-:togointocommandmode,and“wq”tosavethechangestothefileandquit.

Inthisprogram,inthesecondlinewearedeclaringthevariableinPintoholdthepinnumberwherethebuttonisconnected.Inthenexttwolines,wesetthispintobeaninput,andaskingtheRPitousephysicalpinreferences.

Insidethewhileloop,wetakeareadingfromtheinPinusingtheGPIO.inputmethod.Ifthevalueistrue,wewillprint“NotPressed”,otherwisewewillprint“Pressed”.

WhentheGPIO.inputreadsaHIGH(anythingaround3V),thenitwillreturnaTRUEvalue,otherwiseFALSE.

Onthecommandline,runtheprogram:>pythonbutton.py

Hint:iftheRPicomplainslikethis:Traceback(mostrecentcalllast):

File“button.py”,line1,in<module>

importRPi.GPIOasGPIO##ImportGPIOLibrary

ImportError:NomodulenamedRPi.GPIO

Thenyouhaveprobablyforgottentostartyourvirtualenvironment.Dothatnow(fromyour“working”directory):>.venv/bin/activate

Yourrunningprogramwilllooklikethisintheterminal:

Nowpressthebutton,andseethenewmessagescrollingup:

Page 37: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Thereyouhaveit!YounowknowhowtouseGPIOpinsasinputsandoutputs!

Toendtheexecutionoftheprogram,typeControl-C.

Step2.11Alittleexercise:canyouaddanLEDtothebreadboardandgetittolightupwhenyouarepressingthebutton?

Page 38: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERFIVEUseaDHT22sensor

InthissectionwewillconnectaDHT22temperatureandhumiditysensor.Dodothis,wewillinstalltheDHTlibraryfromitsGithubrepository.ThelibraryiswrittenandmaintainedbyAdafruit,andisavailableatthislocation:github.com/adafruit/Adafruit_Python_DHT

TheprocessinvolvesinstallingGitonyourRPi.GitisaversioncontrolsystemthatGithubusestomanagetherepositoriesthatarestoredonit.

Let’sbegin!

Step3.1LogontoyourRPiifyouarenotalready,gotoyourworkingfolder,andactivateyourPythonvirtualenvironment:>[email protected]

>cd/var/working

>.venv/bin/activate

Yourcommandpromptnowhasthe“(venv)”flagshowing:(venv)root@raspberrypi:/var/working#

Step3.2InstallGit:>apt-getinstallgit-core

Thisisalargepackage,soinstallationwilltakeawhile.

Step3.3ConfigureGitbytellingitwhoyouare:>gitconfig—globaluser.name“Peter”

>gitconfig—[email protected]

Toconfirmyoursettings,typethis:>gitconfig—list

user.name=Peter

[email protected]

Page 39: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step3.4WecannowgoaheadandclonetheAdafruitDHTlibraryfromGithub.Useyourbrowsertovisitthelibrarypage:github.com/adafruit/Adafruit_Python_DHT

Ontherightsideofthepage,noticethecloneURLtextbox.ClickontheclipboardicontocopytheURLintoyourclipboard.

Step3.5OnyourRPi,typethistoclonethelibraryrepositorytoyourcomputer:>gitclonehttps://github.com/adafruit/Adafruit_Python_DHT.git

Cloninginto‘Adafruit_Python_DHT’…

remote:Countingobjects:112,done.

remote:Total112(delta0),reused0(delta0),pack-reused112

Receivingobjects:100%(112/112),38.30KiB,done.

Resolvingdeltas:100%(66/66),done.

Note:IusedCommand-VontheMacorCtr-VonWindows/LinuxtopasttheURLfromtheclipboardintothecommandline.

Step3.6Younowhaveanewdirectoryinyourworkingfolder,namedAdafruit_Python_DHT.

Changeinit,andrunthesetupscripttoinstallthelibrary.>cdAdafruit_Python_DHT

>pythonsetup.pyinstall

Step3.7

Page 40: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Thelibraryrepositorycomeswithexamples,let’susethemtomakesurethatoursensorworks.Firstthough,weneedtoconnectoursensor.Pluginthesensortoyourbreadboard.Inmyexample,IhaveleftthecircuitfromtheearlierLEDexperimentintactincaseIwanttouseitlater.Don’tworryifitlooksabitmessy,thereareonly4connectionstobemade.

Page 41: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step3.8LookattheDHT22sensorfromthefront:

Page 42: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Hereisthepurposeofeachpin:

1. 3.3V2. Data3. Leaveunconnected4. GND

Startbytakinga10KOhmresistorandconnectingitbetweenpins1and2.Thisresistorwillbeapullupforthedatapin.

Next,connectpin2to3.3V.Ifyoualreadyhaveajumperwireconnectedtopin1ontheRPi,thenfollowthiswiretothebreadboardandconnectyoursensorpowerwiretoafreeholeinthesamecolumnasthewirethatiscomingfromtheRPi.

Similarly,connectawirefromthesensorpin4toanavailablegroundpinontheRPi,ortoagroundcolumnonthebreadboard.

Finally,let’sconnectthedatapin.IwouldnormallyconnectthedatapintotheRPiGPIO4,whichisthephysicalpin7.However,pin7isalreadyconnectedtotheLED,soIwilluseGPIOpin17instead,whichisphysicalpin11.

Double-checktheconnections,andlet’smoveonthethePythonprogramsothatwecantakeameasurement.

Page 43: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

ThisinteractivepinoutdiagramcreditGadgetoid,pi.gadgetoid.com/pinout

Page 44: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step3.9ChangeintotheexamplesfolderoftheAdafruitPythonDHTlibrary.Forme,thisisdonelikethis:>cd/var/working/Adafruit_Python_DHT/examples

TheexampleIwanttouseistitled“AdafruitDHT.py”.Let’shavealookinsideoutofcuriosity,toseehowitworks.We’llusecattodothis:>catAdafruitDHT.py

Noticehowthereisanarraythatcontainsthevalidsensornames.Theprogramalsoreadsthesensordatapinfromthecommandline.

ThereadingisdonewiththeinstructionAdafruit_DHT.read_retry(sensor,pin).Thisreturnsthehumidityandtemperaturevalues,whichareassignedtolocalvariablesthroughmultipleassignment.

Let’sruntheprogram!>pythonAdafruitDHT.py230217

Temp=25.9*CHumidity=57.1%

Itworked!Themodelofmysensoris2302,andIhaditconnectedtoGPIO17.TakecarenottoconfusetheGPIOnumberwiththephysicalpinnumberbecausetheyaredifferent.UsethepinmapabovetofigureoutyourGPIO.

Curiously,thisprogramworkedwithmysensorbothwhenIuse2302or22asthenameargument.ThereadingsIreceivedineithercasewerealmostidentical,soIguessthatthetwosensortypesaredirectlycompatible.Buttheprogramdidn’tworkwhenIused11asthesensorname.

Awesome!WehavesetupourRPi,andwecanuseit’sGPIOs.WeevenhavealibrarytomakeiteasytoworkwiththeDHTsensor.

Let’smoveontosettingupawebapplicationserversothatwecanmultiplyourRPi’sfunctionalityandgetourdatatothecloud!

Page 45: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERSIXCreateawebapplicationstackontheRPi

Atthispoint,wehavesetuptheRaspberryPiwithanoperatingsystemandPython.WecanconnectdevicestoitsGPIOsandreadthestatusofabutton,controlanLED,andtaketemperatureandhumidityreadingsfromasensor.

Next,youwilllearnhowtousetheWebasaninterfacefortheRPi.Thisway,youwillbeabletocontrolfunctionalityontheRaspberryPiviaawebbrowser.

TheWebapplicationstack.Eachlayerimplementsafunctiononwhichsoftwareonthelayeraboveitdepends.Thisdiagramalsoshowsthespecificsoftwarethatwewilluseinsideeachlayer(theblueboxes),thehardware(theredbox)

andtheclientcomponents(thegreenboxes).

Todothis,wewillcreatesomethingcalleda“webapplicationstack”.Thestackcontainssoftwarethat:

allowsawebbrowsertointeractwiththeRPi(thisisthewebserversoftware),allowsforPythonscriptstobeexecutedinresponsetowebbrowserrequests(anapplicationserver)makesiteasyforustocreateawebapplication,thatis,anapplicationthatwecaninteractwithviaourwebbrowser(awebapplicationframework).

There’sothersoftwarecomponentswewillneed,likeadatabasetostoreandretrievedatafromoursensors,andrelatedlibraries.

Let’sgetstartedwiththewebserver.WewilluseNginx,whichisaverypopularserver,fastandwithasmallmemoryanddiskfootprintthatisverypopularforrunningonsmallcomputersliketheRPioronvirtualmachines.

Then,wewillinstallFlask,aPythonwebdevelopmentmicro-frameworkthatmakesiteasytobuildgreatwebapplicationsquickly.

Page 46: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Next,we’llinstallaPythonapplicationserver,uWSGI,whichwillexecuteourPythonprogramsonbehalfoftheNginxwebserver.

Step4.1LogontoyourRPiifyouarenotalready,gotoyourworkingfolder,andactivateyourPythonvirtualenvironment:>[email protected]

Step4.2InstallNginx:>apt-getinstallnginx

Step4.3Installationwilltakeawhile.Onceit’sdone,youcantestyournewwebserver.

First,rememberwhatyourIPaddressis(incaseyouforgotsinceyouloggedonviaSSH!).Hint:usetheifconfigcommand.

StartNginx:>/etc/init.d/nginxstart

Startingnginx:nginx.

Page 47: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step4.4Gotoyourwebbrowser,andnavigatetoyourRPi’sIPaddress:http://192.168.111.63

YoushouldseeNginx’sdefaulthomepage:

Step4.5Wehaveaworkingwebserver!Let’smoveontotherestofthestack.BeforeweinstallFlaskanduWSGI,we’llcreateanewdirectorythatwillbethehomeofourwebapplication.Init,wewillinstalladedicatedPythonvirtualenvironment,justlikewedidearlierwiththesimpleLEDandDHTexamples.

We’llsetupthisdirectoryinthe/vardirectory.Within/varlet’screateasubdirectorycalledwww,andwithinthatonecalledlab_app.Ifyoudecidetocreatemorewebapplicationslater,youcanputtheminthewwwtoo.Ifyoudon’tlikethenameIchose,“lab_app”,chooseonethatyoudo.>cd/var

>mkdirwww

>cd/www

>mkdirlab_app

Page 48: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step4.6Nowwe’llsetupaPythonvirtualenvironmentinlab_app.>cdlab_app

>virtualenvvenv

Newpythonexecutableinvenv/bin/python

Installingdistribute…………….done.

Installingpip……………done.

Step4.7Activatethenewvirtualenvironment:>.venv/bin/activate

(venv)root@raspberrypi:/var/www/lab_app#

Onceactivated,yourcommandpromptwillcontainthenameofyourvirtualenvironmentatthebeginningoftheprompt(“venv”).

Step4.8OntoFlask!Remember,Flaskisamicro-frameworkthatmakesiteasytocreatewebapplicationusingPython.ItselfisaPythonpackage,sowewillinstallitusingthepippackagemanager:>pipinstallflask

Eventually,youwillseethissuccessmessage:SuccessfullyinstalledflaskWerkzeugJinja2itsdangerousmarkupsafe

Cleaningup…

Page 49: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step4.9Let’stestFlasktomakesureitisreallyworking.

Usevimandcopythisprograminit(hereitisonGithub):>vimhello.py

…thentype“i”togetintoinsertmode.fromflaskimportFlask

app=Flask(__name__)

@app.route(“/”)

defhello():

return“HelloWorld!”

if__name__==“__main__”:

app.run(host=‘0.0.0.0’,port=8080)

Saveitashello.pyusingESC-wqandENTER,andthenrunit:>pythonhello.py

*Runningonhttp://0.0.0.0:8080/(PressCTRL+Ctoquit)

Flaskisnowwaitingforabrowsertomakearequest.Itwilloutputlogmessagestotheconsolesothatyoucanseewhatishappening.

GotoyourbrowserandnavigatetothisURL:http://192.168.111.63:8080(changetheIPaddresstotheactuallIPaddressofyourRPi,butkeepthe8080portnumberthesame).Youshouldseethis:

ToexitFlaskandcontinuewithoursetup,typeCtrl-Cinthecommandline.

Page 50: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step4.10Next,it’stheturnoftheuWSGIapplicationserver.ThiswillexecutePythonscriptsonbehalfofthewebserver.ItisalsoaPythonpackage,soinstallitlikethis:>pipinstalluwsgi

ThistakesawhilebecausetheuWSGIisactuallycompilerfromsourceonyourRPi.Eventually,itwillsaythisandfinish:Successfullyinstalleduwsgi

Cleaningup…

Step4.11NowweneedtoconfigureNginxanduWSGItoworktogether.

We’llstartwithNginx.Firstremovetheconfigurationfileforthedefaultapplication(theonethatproducedtheHelloWorldpageinourtestearlier),thencreateanewapplicationconfigurationfileforournewapplication,andenableit.

Then,gotouWSGI.Createaconfigurationfileforit,andmakeitsothatitsservicestartsautomaticallywhentheRPistarts.Thismayseemabitcomplex,butIassureyouitnothingyoucan’thandle.

Page 51: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step4.12Nginx:removethedefaultapplicationconfigurationfile.Thisfileisin/etc/nginx/sites-enabled/default.Thefile“default”isactuallyasymboliclink(likeaWindowsshortcut),pointingto/etc/nginx/sites-available/default.

Justremovethislinkandwearedone:>rm/etc/nginx/sites-enabled/default

Thencreateanewconfiguration.Createafilecalledlab_app_nginx.conf:>cd/var/www/lab_app/

>vimlab_app_nginx.conf

Press“i”togointoinputmode,andcopythiscode:server{

listen80;

server_namelocalhost;

charsetutf-8;

client_max_body_size75M;

location/static{

root/var/www/lab_app/;

}

location/{try_files$uri@labapp;}

location@labapp{

includeuwsgi_params;

uwsgi_passunix:/var/www/lab_app/lab_app_uwsgi.sock;

}

}

Youareconfiguringanapplicationthatlistensforrequestsonport80.Thelocationofitsstaticassets(thingslikeHTMLfiles,CSSfilesandimages)areinadirectorycalled“static”,whichisinsidetherootfolder.Therootfolderisat/var/www/lab_app.

TheconnectiontotheuWSGIapplicationserverisdoneviaasocketfile.ThiswillbegeneratedlaterbyuWSGI,butnowwecandefineitslocationat/var/www/lab_app/labapp_uwsgi.sock.

Step4.13Toenablethisnewapplicationwefirstcreateasymboliclinkfromitsreallocationto/etc/nginx/conf.d/.,andthenwewillrestartNginxtomakethechangeseffective.Nginxalsolooksin/etc/nginx/conf.d/tofindconfigurationfilesforapplications,inadditionto/etc/nginx/sites-enabled.

Createthesymboliclink:>ln-s/var/www/lab_app/lab_app_nginx.conf/etc/nginx/conf.d/

Step4.14RestartNginxtomakethechangeseffective:>/etc/init.d/nginxrestart

Restartingnginx:nginx.

Page 52: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step4.15Let’sswitchtouWSGI.Createanewfile:>vim/var/www/lab_app/lab_app_uwsgi.ini

…andcopythiscodeinit:[uwsgi]

#application’sbasefolder

base=/var/www/lab_app

#pythonmoduletoimport

app=hello

module=%(app)

home=%(base)/venv

pythonpath=%(base)

#socketfile’slocation

socket=/var/www/lab_app/%n.sock

#permissionsforthesocketfile

chmod-socket=666

#thevariablethatholdsaflaskapplicationinsidethemoduleimportedatline#6

callable=app

#locationoflogfiles

logto=/var/log/uwsgi/%n.log

Therearetwothingstonotice.

First,thenameofourappis“hello”,whichmatchesthenameofourtexthello.pyprogramthatwecreatedearlier.

Second,thelastlineisforthelogfile,whichwillbestoredin/var/log/uwsgi.Thisdirectorydoesnotexist,soweneedtocreateit:>mkdir-p/var/log/uwsgi

Page 53: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step4.16Let’strytostarttheuWSGIdaemonnow:>uwsgi—ini/var/www/lab_app/lab_app_uwsgi.ini

[uWSGI]gettingINIconfigurationfrom/var/www/lab_app/lab_app_uwsgi.ini

Thedaemonwillstart,andjustlikeourtestoftheFlaskserverearlier,itwilloutputlogmessagetotheconsole.

YoucanverifythatuWSGIviaNginxandFlaskisrespondingtoyourwebbrowserrequestsbypointingyourbrowsertohttp://192.168.111.63/(changemyIPaddresstoyours):

Youcanseethatthepageatport80isnowreturningfromourhello.pyprogram.

Atthispoint,sincewedidn’tgetanyerrormessages,wecanbeconfidentthatuWSGIworksfine.Tomakeituseful,wemustmakethedaemonrunasabackgroundservice.

HitCtrl-Ctodothisnext.

Page 54: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step4.17DebiancomeswithaservicemanagementsystemcalledUpstart.UpstartcanbeusedtodothingslikestartupaservicewhentheRaspberryPirestarts,ortorestartitwhenitcrushes.uWSGIworkswellwithUpstart.InuWSGI,thereisaspecialmodecalledEmperor,whichmanagesapplicationinstances.Emperorwilllookforconfigurationfilesthatexistinafoldercalled,appropriately,“vassals”.Emperorthenwillspawninstancesoftheapplicationserverbasedonthosefiles.

Inshort,UpstartwillstartEmperor,andEmperorwillstartouruWSGIapplicationserverbasedonavassalconfigurationfile.

FirstinstallUpstart:>apt-getinstallupstart

…andreboot:>reboot

Step4.18OncetherebootiscompleteandyouhaveloggedbackintotherootaccountofyourRPi,createtheUpstartconfigurationfile.Thisshouldgoin/etc/init,sincethisiswhereUpstartlooksforconfigurationfiles:>vim/etc/init/uwsgi.conf

Copythefollowingcodetothisfile:description“uWSGI”

startonrunlevel[2345]

stoponrunlevel[06]

respawn

envUWSGI=/var/www/lab_app/venv/bin/uwsgi

envLOGTO=/var/log/uwsgi/emperor.log

exec$UWSGI—master—emperor/etc/uwsgi/vassals—die-on-term—uidroot—gidroot—logto$LOGTO

Noticethatthelastline,startingwith“exec”islookingforEmperorconfigurationfilesin/etc/uwsgi/vassals.Solet’screateaconfigurationfileforourapplicationintherenow.

Step4.19Createthevassalsdirectory:>mkdir-p/etc/uwsgi/vassals

Noticethatthe“p”switchinstructsthemkdircommandtocreateanydirectoriesbelow“vassals”thatdon’texist.So“uwsgi”and“vassals”willbecreatedfromasinglecommand.

Step4.20Now,wewilllinktheuWSGIconfigurationfilewecreatedinstep15inside/var/www/lab_app/lab_app_uwsgi.initothevassalsdirectoryviaasymboliclink:>ln-s/var/www/lab_app/lab_app_uwsgi.ini/etc/uwsgi/vassals/

Page 55: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step4.21Let’strytostartuWSGIviaUpstartnow:>startuWSGI

Step4.22Verify.DirectyourbrowsertoyourRPi,andyoushouldseethefamiliarHelloWorldmessage:

Step4.23Trouble?Problems?Logfilesareheretohelp.Wheneversomethingisnotquiteright,startbyexaminingthelogfiles.Youhavetwo:

1. Nginxaccessat/var/log/nginx/access.log2. Nginxerrorat/var/log/nginx/error.log3. uWSGIat/var/log/uwsgi/lab_app_uwsgi.log

Step4.24YourapplicationshouldalsobeabletostartautomaticallyafteraRPireboot.TryrebootingyourRPi,waitforacoupleofminutesfortheprocesstocomplete,andwithoutloggingon,requesthttp://192.168.111.63inyourbrowser.Thepageshouldbeloadingwithoutanyissues.

Page 56: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERSEVENServingstaticassets

InthissectionIwillshowyouhowtoservestaticwebassetsfromyourRPi.Staticassetsarefileslikeimages,CSSandJavascript,oranythingthatisnotexecutablecode.

Inthissection,wewillsetupNginxtoservestaticassets,andmakeourhumble“HelloWorld!”messagealittlebitnicertolookatusingsomeCSS.

Step5.1Wewillcreateanewdirectoryinthe/var/www/lab_appdirectoryinwhichwewillstorestaticassets.Let’scallthisnewdirectory“static”:

(AssumingyouhaveloggedontoyourRPiandarenowinthe/var/www/lab_appdirectory):>mkdirstatic

>cdstatic

>mkdircss

>mkdirimages

Step5.2Let’sseeanexampleofastaticfile.First,let’screateasimpleHTMLfile,andstoreitinthestaticdirectorywiththename“a_static_file.html”.Giveitthiscontent:<html>

<head>

<title>Staticpage</title>

</head>

<body>

<h1>Thisisanexampleofastaticpage</h1>

<p>Neat,isn’tit?</p>

</body>

</html>

Page 57: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step5.3Navigateyourbrowsertohttp://192.168.111.63/static/a_static_file.html.

Youshouldseethis:

Page 58: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step5.4Let’saddaCSSfiletomakethispagelookabitbetter.IfyouarenotfamiliarwithCSS,fornowitisenoughtoknowthatCSSisalanguageforstylingwebpages.Itcontainsinformationthatthebrowserusestochangethedefaultwayitrendersthevariouswebpageelements.

Inthisproject,Ihaveselectedasimpleandpopularboilerplatedesigncalled“Skeleton”.Youcanfinditanddownloaditathttp://getskeleton.com.ThedownloadedarchivecontainstwoCSSfilesandseveralimagestomatchthestyles.

DownloadtheSkeletonZIPfileandexpandit.Youwillhavesomethinglikethis:

Thearchivecontainsanexampleindexfileinitsroot,thetwoCSSfilesinsidethecssdirectory,andafaviconimagefileinimages.

Page 59: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step5.5YounowneedtotransferthesefilestotheappropriatelocationsonyourRPi.TheeasiestwaytodothisistouseanSFT/SSHutility.MyfavouriteutilityisCyberduck(http://cyberduck.en.softonic.com/mac)ontheMac,andFilezilla(http://filezilla-project.org)onWindows.

Let’sworkwithCyberduck.Startitup,andclickontheOpenConnectionicon.ChooseSFTPfromtheprotocoldrop-downmenu.TypeintheRPiaddressandcredentials,likeintheimagebelow:

Click“Connect”.Youwillgetawindowaskingyoutoaccepttheserverfingerprint,towhichyoushouldclick“Allow”.

TheprocessofcopyingfilesusingSFTPisverysimilaracrossdifferentgraphicalFTPclients.

Step5.6

Page 60: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Navigatetothestaticdirectoryofyourapplication,likeinthisimage:

Now,copythefilesfromthecssdirectoryintheSkeletonarchive(whichisonyourlocalcomputer)tothecssdirectoryonyourRPi(acceptanyrequestforconfirmation):

Page 61: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Dothesamefortheimagefile.Don’tworryabouttheHTMLfile.

Step5.7OntheRPi,youshouldnowhavethesefilesinyourstaticdirectory:

Page 62: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section
Page 63: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step5.8NowyouneedtoconnectyourHTMLfiletotheCSS.Addthefollowingtexttoa_static_file.html(additionsareinred):<html>

<head>

<metacharset=“utf-8”>

<title>Staticpage</title>

<!—MobileSpecificMetas

––––––––––––––––——>

<metaname=“viewport”content=“width=device-width,initial-scale=1”>

<!—FONT

––––––––––––––––——>

<linkhref=”//fonts.googleapis.com/css?family=Raleway:400,300,600”rel=“stylesheet”

type=“text/css”>

<!—CSS

––––––––––––––––——>

<linkrel=“stylesheet”href=“css/normalize.css”>

<linkrel=“stylesheet”href=“css/skeleton.css”>

<!—Favicon

––––––––––––––––——>

<linkrel=“icon”type=“image/png”href=“images/favicon.png”>

</head>

<body>

<h1>Thisisanexampleofastaticpage</h1>

<p>Neat,isn’tit?</p>

</body>

</html>

ThenewcontentprovideslinkstothetwoCSSfiles,tothefaviconimage,andthethefonttypethatwewanttouseinourHTMLpage.

Page 64: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step5.9AssumingyouhavesavedtheupdatedHTMLfile,refreshyourbrowser.Thisiswhatyoushouldseenow:

Muchnicerlookingtext!Thistemplatecontainsmanymoreusefulfeaturesthatwewillexplorelater.

Ok,wehaveastylestaticpage.HowaboutwecreatethesamestylingforourFlask“HelloWorld!”app?Let’sdothisinthenextsection.

Page 65: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTEREIGHTFlasktemplatesandCSSstyling

Ireallydon’tlikeworkingwithuglylookingwebpages,sobeforegoinganyfurtherI’dliketoapplytheSkeletonstyletothepagesthatareservedbyFlask.Atthemoment,there’sonlyone,implementedbythe“hello”methodinthehello.pyfile.

InFlask,youcanstylepagesintwosteps.First,youcreateatemplateforyourpageorpages(onetemplateperpage,oryoucansharetemplatesbetweenmultiplepages).Second,youlinkCSSfilesandimageswiththetemplates.

Let’strythisout.

Step6.1Gotoyourappdirectoryandcreateanewdirectory,called“templates”.>cd/var/www/lab_app

>mkdirtemplates

Step6.2Addsometexttothehello.pyprogram.Theaddedtextisinred:fromflaskimportFlask

fromflaskimportrender_template

app=Flask(__name__)

app.debug=True

@app.route(“/”)

defhello():

returnrender_template(‘hello.html’,message=“HelloWorld!”)

if__name__==“__main__”:

app.run(host=‘0.0.0.0’,port=8080)

First,weareimportingthepackagethathandlestemplates.Inthesecondredinsert,weareenablingtheFlaskverbosemodesothatFlaskproducesmoredebugoutputtohelpustroubleshootproblemslateron.

Then,inthethirdredline,weareconnectingatemplatecontainedinfile“hello.html”(whichwewillcreateinaminuteinsidethetemplatesdirectory),andpassingavariablenamed“message’withsometexttoit.

Page 66: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step6.3Timetocreatetheactualtemplate.Changeinthetemplatesdirectory,andcreateanewfilenamed“hello.html”withthefollowingcode:<html>

<head>

<metacharset=“utf-8”>

<title>Staticpage</title>

<!—MobileSpecificMetas

––––––––––––––––——>

<metaname=“viewport”content=“width=device-width,initial-scale=1”>

<!—FONT

––––––––––––––––——>

<linkhref=”//fonts.googleapis.com/css?family=Raleway:400,300,600”rel=“stylesheet”

type=“text/css”>

<!—CSS

––––––––––––––––——>

<linkrel=“stylesheet”href=”/static/css/normalize.css”>

<linkrel=“stylesheet”href=”/static/css/skeleton.css”>

<!—Favicon

––––––––––––––––——>

<linkrel=“icon”type=”/static/image/png”href=“images/favicon.png”>

</head>

<body>

<h1>{{message}}</h1>

</body>

</html>

ThislookssimilartotheHTMLpageinthestaticdirectory.Itis.Theonlydifferenceisthat:

Ihavereplacedthecontentoftheh1tagwithavariableplaceholder,usingthecurlybrackets.InsidethecurlybracketsIhaveplacedthenameofthevariable(“message”),thatIhavepassedtoitfromthePythonhello.pyprogram.IhaveadjustedthepathoftheCSSandimagefilestomatchtheweblocationofthesefiles.

AtemplatelanguagecalledJinja2willprocessthecontentofthecurlybrackets,andinthisexamplewillreplaceitwiththeactualvalueofthevariable.

Step6.4BecausewehavemadechangestothePythonprogram,weneedtorestarttheapplicationserver,uWSGI.Dothatlikethis:>restartuwsgi

Page 67: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step6.5Refreshyourbrowser:

Thereyougo,amuchnicerhelloworld!

Younowhaveatemplateconnectedtoanicestylesheet.

Ifyouranintoproblems,thingslikeInternalServererrorsandthelike,keepthesepointsinmind:

AquickwaytotroubleshootistorunyourFlaskapplicationdirectly,insteadviauWSGI.Whileattherootofyourapplication,use“pythonhello.py”tostartit.Youwillneedtopointyourbrowsertohttp://192.168.111.63:8080/,sincethisiswhereyourFlaskapplicationisserved(noticetheportID,inred).Ifthereisaproblemwithyourapplication,thecomprehensiveerrormessageswillhelpyoutofindouttheproblem.WheneveryoumakeachangetothePythonortemplatefiles,youmustrestarttheserverinorderforthechangestobecomeeffective.Dothiswith“restartuwsgi”ifyouareusinguWSGI,orbyhittingCtrl-Candrunning“pythonhello.py”ifyouareusingFlaskdirectly.

Awesome!Nextup,let’sgetthesensordataaccessiblethroughthewebbrowser!

Page 68: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERNINEViewsensorreadingsviaawebbrowser

WealreadyknowhowtoworkwiththeDHTsensorusingastand-alonePythonscript.Inthissection,youwilllearnhowtoworkwiththissensorfromwithinaFlaskapplication.

Asyouwillsee,thisisrelativelysimple.YouhavetoinstalltheDHTlibraryinthePythonvirtualenvironment(whichiswhatyouhadtodowiththestand-alonePythonscriptanyway),getreadingsfromthesensorandpassthemtothetemplatefile.

Step7.1First,let’sinstalltheDHTsensorPythonlibrarytothePythonvirtualenvironmentthatweuseforourFlaskapplication.

Iassumethatyouareloggedin.

Awhileago,wedownloadedtheAdafruitDHTlibrary,andsaveditin/var/working/Adafruit_Python_DHT.Wearegoingtousethatpackageagain,butthistimewe’llinstallitinthe/var/www/lab_app/venvvirtualenvironment.>cd/var/working/Adafruit_Python_DHT

>/var/www/lab_app/venv/bin/pythonsetup.pyinstall

Withthesecondcommand,weareusingthePythoninterpreterinourtargetvirtualenvironmenttoexecutethesetupscript.Asaresult,thelibraryisinstalledinthetargetvirtualenvironment.

Step7.2SwitchbacktoourFlaskapplicationdirectory.We’llreplacethehello.pyscriptwithanewone.Thenewscript,calledlab_app.py,willcontainasinglemethod.ThismethodwillgetreadingsfromthesensorandreturnaHTMLpagetotheuser.>cd/var/www/lab_app

>vimlab_app.py

Page 69: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step7.3Copythefollowingscriptintheeditor(filenameonGithub:lab_app_v1.py):fromflaskimportFlask,request,render_template

app=Flask(__name__)

app.debug=True#MakethisFalseifyouarenolongerdebugging

@app.route(“/”)

defhello():

return“HelloWorld!”

@app.route(“/lab_temp”)

deflab_temp():

importsys

importAdafruit_DHT

humidity,temperature=Adafruit_DHT.read_retry(Adafruit_DHT.AM2302,17)

ifhumidityisnotNoneandtemperatureisnotNone:

returnrender_template(“lab_temp.html”,temp=temperature,hum=humidity)

else:

returnrender_template(“no_sensor.html”)

if__name__==“__main__”:

app.run(host=‘0.0.0.0’,port=8080)

Thewebrootofourwebapplicationstillreturnsthe“HelloWorld”message.Ihaveaddedanewmethod,“lab_temp”,whichisrevokedwhenthebrowsermakesagetrequesttothelab_tempresource.So,thismethodwillrespondtothisaddressinyourbrowser:http://192.168.111.63/lab_temp

(ofproject,theexactIPaddresscouldbedifferentforyourRPi).

Insidethismethod,notethatweareimportingthenecessarylibraries,andgetthevaluesfromthesensor.Ifvaluesareretrievedsuccessfully,theyarepassedtothelab_temp.htmltemplate,butifnotthescriptwillreturntheno_sensor.htmltemplate.

Page 70: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step7.4Gointhetemplatesdirectoryandcreatethefirsttemplate,lab_temp.html.Itshouldcontainthiscode(onGithub,thisfileisnamed“lab_temp_v1.html”):<html>

<metacharset=“utf-8”>

<title>LabConditionsbyRPi</title>

<metahttp-equiv=“refresh”content=“10”>

<metaname=“description”content=“Labconditions-RPi”>

<metaname=“author”content=“PeterDalmaris”>

<!—MobileSpecificMetas

––––––––––––––––——>

<metaname=“viewport”content=“width=device-width,initial-scale=1”>

<!—FONT

––––––––––––––––——>

<linkhref=”//fonts.googleapis.com/css?family=Raleway:400,300,600”rel=“stylesheet”

type=“text/css”>

<!—CSS

––––––––––––––––——>

<linkrel=“stylesheet”href=”/static/css/normalize.css”>

<linkrel=“stylesheet”href=”/static/css/skeleton.css”>

<!—Favicon

––––––––––––––––——>

<linkrel=“icon”type=“image/png”href=”/static/images/favicon.png”>

</head>

<body>

<divclass=“container”>

<divclass=“row”>

<divclass=“two-thirdcolumn”style=“margin-top:5%”>

<h2>Realtimelabconditions</h2>

<h1>Temperature:{{“{0:0.1f}”.format(temp)}}°C</h1>

<h1>Humidity:{{“{0:0.1f}”.format(hum)}}%</h1>

<p>Thispagerefreshesevery10seconds</p>

</div>

</div>

</div>

</body>

</html>

Inred,ImarkthatlocationswherethevariablesthatarepassedbythePythonscriptareinterpretedandreplacedwiththeiractualvalues.TherestofthisHTMLcodeisalmostidenticaltothecodefromthehello.pytemplatewesawearlier.

Atthetopofthepage,Ihaveaddedametatagthatinstructsthebrowsertorefreshthepageevery10seconds.Thisisprobablytheeasiestwaytocauseapagetoself-refresh,thereforekeepingthereadingscurrentonourscreen.

Page 71: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step7.5Stillinthetemplatesdirectory,createtheno_sensor.htmlfilewiththiscontent(onGithub,thisfileisnamed“no_sensor_v1.html”):<html>

<head>

<linkrel=“stylesheet”type=“text/css”href=”/static/style/eureka_style.css”>

</head>

<divclass=‘test_container’>

<h1>Sorry,can’taccessthesensor!</h1>

</div>

</html>

Step7.6WehavetoupdatetheuWSGIconfigurationfiletopointtothenewPythonapplicationfile,lab_app.py(onGithub,thisfileisnamed“lab_app_uwsgi_v2.ini”).

Openitandmaketheupdate(updatedtextinred):>vimlab_app_uwsgi.ini

Content:[uwsgi]

#application’sbasefolder

base=/var/www/lab_app

#pythonmoduletoimport

app=lab_app

module=%(app)

home=%(base)/venv

pythonpath=%(base)

#socketfile’slocation

socket=/var/www/lab_app/%n.sock

#permissionsforthesocketfile

chmod-socket=666

#thevariablethatholdsaflaskapplicationinsidethemoduleimportedatline#6

callable=app

#locationoflogfiles

logto=/var/log/uwsgi/%n.log

Step7.7RestartuWSGItomakethechangeeffective:>restartuwsgi

Page 72: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step7.8GotoyourbrowserandtypeinthisURL:http://192.168.111.63/lab_temp

Youshouldseethisresult:

Nice,isn’tit?

There’snotimeforrestforthewicked,sowhatarewegoingtoworkonnext?

Howaboutinstallingadatabasesothatwecanstoreourreadingsinit?Wecanuseadatabasetologdata,thenretrieveitandcreatetablesandgraphs.That’scomingupnext!

Page 73: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERTENSetupasimpledatabaseanduseittologsensordataInthissection,wewillinstallasimpledatabaseanduseittostoresensorreadings.ThedatabasewewilluseisSQlite,averypopularopen-sourcerelationaldatabasethatwasdevelopedforuseinembeddeddevices.MostsmartphoneapplicationsuseSQliteforatleastpartoftheirdatamanagementrequirements.OntheRPi,althoughyoucaninstallamore“heavyduty”databaselikeMySQLorPostgresql,forourrequirementsofalowtrafficdatabasedrivenapplication,SQLiteisjustperfect.

Inthislecture,IwillshowyouhowtoinstallSQLite3,andinteractwithitonthecommandline.

Later,wearegoingtocreateaPythonscriptthattakesareadingfromthesensorandstoresthevaluesinthedatabase.Wewillalsocreateascheduler(“Cron”)taskthatwillinvokethescriptevery10minutesautomatically.Thisway,ourRPiwilltaketemperatureandhumiditymeasurementsevery10minutes,forever(oratleastuntilitrunsoutofdiskspaceorweturnoffpowerorwestoptheCrontask).

Step8.1AssumingyouareloggedontoyourRPi,installSQLite:>apt-getinstallsqlite3

Step8.2Testitonthecommandline.From/var/www/lab_app:>cd/var/www/lab_app

>sqlite3lab_app.db

sqlite>.help

ThefirstlinestartstheSQLitecommandlineinterfacewiththenameofthedatabasewewanttouse.Thisdatabasefileisemptyatthemoment,butwewilladdacoupleoftablesandrecordsinit.

Thesecondlinewillcausealistwiththeavailablecommandswillappear.InSQLite,youcaninputmulti-linestatementsbystartingwiththeBEGIN;commandandendingwiththeCOMMIT;command.Everycommandshouldendwithasemicolon(“;”).

Step8.3Let’screatethedatabasetablewherewewillstorethetemperaturedata,andmanuallycreateacoupleofrecordsinit(continuingfrom2,stillontheSQLitecommandprompt):sqlite>begin;

sqlite>createtabletemperatures(rDatetimedatetime,sensorIDtext,tempnumeric);

sqlite>insertintotemperaturesvalues(datetime(‘now’),“1”,25);

sqlite>insertintotemperaturesvalues(datetime(‘now’),“1”,25.10);

sqlite>commit;

Page 74: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step8.4Let’sretrievethetworecordswejustcreated:sqlite>select*fromtemperatures;

2015-04-0704:47:00|1|25

2015-04-0704:47:25|1|25.1

Step8.5Dothesameforthehumiditytable:sqlite>begin;

sqlite>createtablehumidities(rDatetimedatetime,sensorIDtext,tempnumeric);

sqlite>insertintohumiditiesvalues(datetime(‘now’),“1”,51);

sqlite>insertintohumiditiesvalues(datetime(‘now’),“1”,51.10);

sqlite>commit;

Step8.6Youcanusethe“.tables”commandtoverifythatyouhavecreatedtwotables:sqlite>.tables

humiditiestemperatures

Notethatthesetablesarenotyetstoredonthedisk,onlyinabuffer.Youcanalsouse“.schema”soseethepropertiesofatable:sqlite>.schematemperatures

CREATETABLEtemperatures(rDatetimedatetime,sensorIDtext,tempnumeric);

Step8.7Exitandcheckthatthenewdatabasefileexistsinyour/var/www/lab_appdirectory:sqlite>.exit

>ls

hello.pylab_app.dblab_app.pylab_app_uwsgi.inistaticvenv

hello.pyclab_app_nginx.conflab_app.pyclab_app_uwsgi.socktemplates

InredImarkthenewdatabasefile.

Page 75: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step8.8Let’suseaPythonscripttogetareadingfromthesensorandstorethevaluesinanewdatabaserecord.

Createanewfilecalledenv_log.pywiththiscontent:#!/usr/bin/envpython

importsqlite3

importsys

importAdafruit_DHT

deflog_values(sensor_id,temp,hum):

conn=sqlite3.connect(‘/var/www/lab_app/lab_app.db’)#Itisimportanttoprovidean

#absolutepathtothedatabase

#file,otherwiseCronwon’tbe

#abletofindit!

curs=conn.cursor()

curs.execute(“““INSERTINTOtemperaturesvalues(datetime(‘now’),

(?),(?))”””,(sensor_id,temp))

curs.execute(“““INSERTINTOhumiditiesvalues(datetime(‘now’),

(?),(?))”””,(sensor_id,hum))

conn.commit()

conn.close()

humidity,temperature=Adafruit_DHT.read_retry(Adafruit_DHT.AM2302,17)

ifhumidityisnotNoneandtemperatureisnotNone:

log_values(“1”,temperature,humidity)

else:

log_values(“1”,-999,-999)

Thedatabase-relatedcodeismarkedinred.Thisscriptcontainsamethod,log_values,thatmanagestheprocessofcreatingnewrecordsinthedatabasewiththevaluespassedtoit.

Step8.9Runthenewscripttomakesureitworks.Let’sactivatethePythoninterpreterforourvirtualenvironmentfirst.Alternatively,youcancallourapplication’sPythoninterpreterbytypinginthecompletepathtoitsexecutable.I’lldotheactivationsinceitislesstyping:>.venv/bin/activate

>pythonenv_log.py

Page 76: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step8.10UsetheSqlitecommandlinetooltoverifythatwehaveanewrecordinourdatabase.>sqlite3lab_app.db

SQLiteversion3.7.132012-06-1102:05:22

Enter“.help”forinstructions

EnterSQLstatementsterminatedwitha“;”

sqlite>select*fromtemperatures;

2015-04-0705:13:20|1|24.2999992370605

sqlite>select*fromhumidities;

2015-04-0705:13:20|1|48.7000007629395

sqlite>.exit

Youmayhaveadditionalrecordsinyourdatabase,buttherecordsweareinterestedinherearetheonesjustinsertedbythescriptweexecutedinthepreviousstep.

Allgood,ourPythonscriptcanretrievereadingsfromthesensorandrecordtheminthedatabase.

Step8.11Let’sscheduleameasurementeverytenminutes.WewillusetheLinuxschedulerforthat,Cron.Cronisverysimple.Youuseatexteditortoeditaspecialcronfile.

Foreachtask,youcreatealineoftextthatcontainsthescheduleandthecommandtobeexecuted.TheyimportantthingtorememberistheCrontasksareexecutedwithoutashell,whichmeansthatenvironmentalvariablesarenotretrieved.

Inotherwords,youneedtoalwaysusefullandabsolutepathsforeveryfileyoureferenceinaCronschedule.

StarttheCroneditorlikethis:>crontab-e

Thiswillstartvim.Gotothebottomofthefile(youcanusethe“G”shortcuttogettherefast),gointoinsertmode(“i”)andcopythisscheduleatthatlocation:*/10****/var/www/lab_app/venv/bin/python/var/www/lab_app/env_log.py

The*/10parametermeans“every10minutes”.The“****”means“everyhour,every,minute,everyday,everydayoftheweek”.

NoticethatthepathsarefulltomakesurethattheCrondaemonwillbeabletofindthefilesweneedittoexecute.

Page 77: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step8.12Tenminuteslater,gobackintotheSQlitecommandlineinterfacetoseeifwehaveanewrecord:sqlite>select*fromtemperatures;

2015-04-0705:13:20|1|24.2999992370605

2015-04-0706:00:06|1|24.3999996185303

sqlite>select*fromhumidities;

2015-04-0705:13:20|1|48.7000007629395

2015-04-0706:00:06|1|47.7999992370606

(Newerrecordsinred)

OneproblemwithCronisthatfailuresaresilent.Ifthereisaproblemwithyourscheduleorwithyourscript,andthetaskfails,wewouldnotknowwhy.

Wouldcanonlyknowthatitdidn’tworkbecause,inourpresentinstance,thereisnoadditionalrecordinthedatabase.Ifthishappenstoyou,doubleandtriplecheckyourscriptandCrontask.

Page 78: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step8.13HavingtousetheSQLitecommandlineinterfacetogetthelatestvaluesisnotveryconvenient.Let’sgetourwebapplicationtocreateanewpagewithalistthatcontainstherecordsforthetemperatureandhumiditytables.

First,we’lladdanewmethodinthelab_app.pyfilewhichwillrespondtowebrequestsat/lab_env_db.Openlab_app.pyinvimforediting,andmakeitlikethis(additionsinred-thisisfilelab_app_v2.pyonGithub):fromflaskimportFlask,request,render_template

app=Flask(__name__)

app.debug=True#MakethisFalseifyouarenolongerdebugging

@app.route(“/”)

defhello():

return“HelloWorld!”

@app.route(“/lab_temp”)

deflab_temp():

importsys

importAdafruit_DHT

humidity,temperature=Adafruit_DHT.read_retry(Adafruit_DHT.AM2302,4)

ifhumidityisnotNoneandtemperatureisnotNone:

returnrender_template(“lab_temp.html”,temp=temperature,hum=humidity)

else:

returnrender_template(“no_sensor.html”)

@app.route(“/lab_env_db”)

deflab_env_db():

importsqlite3

conn=sqlite3.connect(‘/var/www/lab_app/lab_app.db’)

curs=conn.cursor()

curs.execute(“SELECT*FROMtemperatures”)

temperatures=curs.fetchall()

curs.execute(“SELECT*FROMhumidities”)

humidities=curs.fetchall()

conn.close()

returnrender_template(“lab_env_db.html”,temp=temperatures,hum=humidities)

if__name__==“__main__”:

app.run(host=‘0.0.0.0’,port=8080)

ThenewcodesubmitstwoSelectstatementstothedatabase.Thedatabasereturnstocollections,oneforthetemperatureandoneforthehumiditytables.Thecodethencallsthelab_env_db.htmltemplateandpassesthetwocollectionstoit.

Step8.14Let’snowcreatethenewtemplate.Usevimtocreateanewfilecalledlab_env_db.htmlandcopythiscodeinit:<!DOCTYPEhtml>

<htmllang=“en”>

<head>

<!—BasicPageNeeds

––––––––––––––––——>

Page 79: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

<metacharset=“utf-8”>

<title>LabConditionsbyRPi</title>

<metaname=“description”content=“Labconditions-RPi”>

<metaname=“author”content=“PeterDalmaris”>

<!—MobileSpecificMetas

––––––––––––––––——>

<metaname=“viewport”content=“width=device-width,initial-scale=1”>

<!—FONT

––––––––––––––––——>

<linkhref=”//fonts.googleapis.com/css?family=Raleway:400,300,600”rel=“stylesheet”

type=“text/css”>

<!—CSS

––––––––––––––––——>

<linkrel=“stylesheet”href=”/static/css/normalize.css”>

<linkrel=“stylesheet”href=”/static/css/skeleton.css”>

<!—Favicon

––––––––––––––––——>

<linkrel=“icon”type=“image/png”href=”/static/images/favicon.png”>

</head>

<body>

<divclass=“container”>

<divclass=“row”>

<divclass=“one-thirdcolumn”style=“margin-top:5%”>

<strong>Showingallrecords</strong>

<h2>Temperatures</h2>

<tableclass=“u-full-width”>

<thead>

<tr>

<th>Date</th>

<th>&deg;C</th>

</tr>

</thead>

<tbody>

{%forrowintemp%}

<tr>

<td>{{row[0]}}</td>

<td>{{‘%0.2f’|format(row[1])}}</td>

</tr>

{%endfor%}

</tbody>

</table>

<h2>Humidities</h2>

<tableclass=“u-full-width”>

<thead>

<tr>

<th>Date</th>

<th>%</th>

</tr>

</thead>

<tbody>

{%forrowinhum%}

<tr>

<td>{{row[0]}}</td>

<td>{{‘%0.2f’|format(row[1])}}</td>

Page 80: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

</tr>

{%endfor%}

</tbody>

</table>

</div>

</div>

</body>

</html>

Thelineswherethetemplateengineiteratesthroughthecollectionobjectsaremarkedinred.

Forexample,thetemperaturecollectionobjectisnamedtemp.Theiteratorwilltakeonerecordatatime,called“row”.Thisrecordisactuallyanormalarray.Wecanpickvaluesoutofthearraybyusingtheindexnumbers.Forexample,“row[1]”willretrievethevaluefromthecellwithindex1,whichisactuallythesecondcell(arraysinPythonarezero-indexed,whichmeansthatthefirstcellisindex0,thesecondisindex1andsoon).

Step8.15RestartuWSGItomakethechangeseffectiveandloadthenewpageinyourbrowser:>restartuwsgi

Youshouldseesomethinglikethiswhenrequestinghttp://192.168.111.63:8080/lab_env_db:

Page 81: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section
Page 82: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERELEVENSelectrecordstoviewbytimeanddate

IfyouleaveyourRPialoneforawhile,itwillcontinuetocapturesensordataandstoringitinthedatabase.Afteradayorso,youwillhave6*24=124recordsfortemperatureandanother124forhumidity.Aweeklater,youwillhave124*7=1008ofeach,andsoon.

Displayingallthatdatainasinglepagewillproduceverylongpages,andwillnotbeveryuseful.ItwouldbereallynicetobeabletogetourRPitoretrieveanddisplayrecordswithinatimeframethatwecanspecify.

Inthislecture,IwillshowyouhowtomakethispossibleintheeasiestwayIcanthinkof.WewillupdateourapplicationsothatyoucansettheURLintheURLofyourbrowser’srequest.Itwilllooklikethis:http://192.168.111.63/lab_env_db?from=2015-04-13&to=2015-04-14.

Step9.1Let’sworkonthedatabasefirst.WewanttodesignanSQLquerythatwillretrieverecordsbeforeandafteradate/timewespecify.Todothis,wewilluseanSQLquerylikethis:SELECT*FROMhumiditiesWHERErDatetimeBETWEEN“2015-04-12”AND“2015-04-13”

Step9.2Let’strythisstatementintheSQLitedatabaseandseeifitworks.LogontoyourRPiifnotalreadyloggedon,andnavigateintoyourapplication’sworkingdirectory:>cd/var/www/lab_app/

>ls-al

Noticethesizeofthedatabasefile:>-rw-r—r—1rootroot76800Apr1403:10lab_app.db

Over76,000bytes!Ithasbeengrowingoverthelastweek.

Step9.3StarttheSQLiteconsole,withthedatabasefileasaparameter:sqlite3lab_app.db

SQLiteversion3.7.132012-06-1102:05:22

Enter“.help”forinstructions

EnterSQLstatementsterminatedwitha“;”

sqlite>

Page 83: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step9.4TryoutthesampleSQLqueryfromabove:sqlite>SELECT*FROMhumiditiesWHERErDatetimeBETWEEN“2015-04-12”AND“2015-04-13”;

2015-04-1200:00:02|1|56.9000015258789

2015-04-1200:10:01|1|56.7000007629395

2015-04-1200:20:02|1|56.9000015258789

2015-04-1200:30:02|1|56.7000007629395

2015-04-1200:40:02|1|56.7000007629395

2015-04-1200:50:01|1|56.2999992370606

2015-04-1201:00:02|1|57

2015-04-1201:10:02|1|57.0999984741211

…(continues)

Thedatabasereturnsrecordsinsertedduringthedatesspecified.

Step9.5Wecanbemorespecificandrequestrecordsrecordedwithminute-levelaccuracy.Forexample:sqlite>SELECT*FROMhumiditiesWHERErDatetimeBETWEEN“2015-04-1218:00:00”AND“2015-04-12

19:00:00”;

2015-04-1218:00:01|1|51.2999992370606

2015-04-1218:10:02|1|51.2000007629395

2015-04-1218:20:02|1|51.2999992370606

2015-04-1218:30:02|1|51.2000007629395

2015-04-1218:40:01|1|51.2999992370606

2015-04-1218:50:02|1|51.2999992370606

ThisstatementreturnedrecordscreatedonApril12,2015,between6pmand7pm.

YoucanexittheSQLiteconsole:sqlite>.exit

Step9.6Ok,wehaveawaytogettherecordsweneedoutofthedatabase.Wenowneedawaytogetthedate/timesintotheURL,alongsideourbrowserGETrequests,andintothePythonscript.Andwewanttodothisinthelab_env_dbmethodsincethatmethodisalreadysetuptoretrievedatafromthedatabase.

Weshouldbeabletogetallthisdonewithminimalfuss.

Openuplab_app.py.Inthetopofthefile,addthefollowingimportstatementssothatitlookslikethis(thisislab_app_v3.pyonGithub):fromflaskimportFlask,request,render_template

importtime

importdatetime

Wewillbeworkingwithtimeanddates,soweareimportingthetimeanddatetimepackages.

Page 84: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Next,wewillworkonlyinmethodlab_env_db(),soIamonlyshowingthatpartofthecodehere.Editthatmethodsothatitlookslikethis(thisisalsoinlab_app_v3.pyonGithub):@app.route(“/lab_env_db”,methods=[‘GET’])

deflab_env_db():

from_date_str=request.args.get(‘from’,time.strftime(“%Y-%m-%d%H:%M”))#Getthefromdate

valuefromtheURL

to_date_str=request.args.get(‘to’,time.strftime(“%Y-%m-%d%H:%M”))#Getthetodatevalue

fromtheURL

importsqlite3

conn=sqlite3.connect(‘/var/www/lab_app/lab_app.db’)

curs=conn.cursor()

#curs.execute(“SELECT*FROMtemperatures”)

#temperatures=curs.fetchall()

#curs.execute(“SELECT*FROMhumidities”)

#humidities=curs.fetchall()

#conn.close()

curs.execute(“SELECT*FROMtemperaturesWHERErDateTimeBETWEEN?AND?”,(from_date_str,

to_date_str))

temperatures=curs.fetchall()

curs.execute(“SELECT*FROMhumiditiesWHERErDateTimeBETWEEN?AND?”,(from_date_str,

to_date_str))

humidities=curs.fetchall()

conn.close()

returnrender_template(“lab_env_db.html”,temp=temperatures,hum=humidities)

Ihavecommentedouttheoldlinesthatrelatetothedatabasesothatyoucaneasilyseewhathaschanged.Asfarasthedatabaseisconcerned,weareconstructingtwonewSQLqueries,oneforthehumiditiestableandoneforthetemperatures,sothattheymatchthequerythatwetestedearlier.

Inordertogetthedate/timerangefromtheURL,intheveryfirstlineofthiscodeIamincludingthe“methods”parameter.Withit,IamtellingPythonthatthismethodcanreceiveGETparametersaspartoftheHTTPrequest.AURLcancontainkey-valuepairsafterthe“?”delimiter,andsuchrequestsarecalled“GET”requests.

Inthe3rdand4thlines,Iusethe“request.args.get”methodtoretrievethevalueswiththe“from”and“to”keysfromtheURL.Iamalsoprovidingdefaultvaluesincasethosearenotprovided.Thisisdonebyusingthepython“time”method,andformattingthevaluethatthismethodintheformatthatthedatabasecanaccept.

Step9.7Let’stryoutthenewcode.Saveandcloseyoureditor,restartuWSGI:>restartuwsgi

…andtypethisURLinyourbrowser:http://192.168.111.63/lab_env_db?from=2015-04-13&to=2015-04-14

Yourbrowserwilldisplaysomethinglikethis:

Page 85: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

ItwillcontainrecordsfromApril142015only.Youcanalsotrytimes:http://192.168.111.63/lab_env_db?from=2015-04-12+13%3A25&to=2015-04-12+15%3A00

…andyouwillgetsomethinglikethis:

Page 86: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

…whereyouonlyhaverecordscreatedonApril14,2015,between13:30and15:00.NoticethattheURLisHTTPencoded,whichmeansthatthewhitespacesandotherspecialcharactershavebeenreplacedbycode.

Youcanuseautilitylikethis:http://www.url-encode-decode.comtoencodeyourURLs.Forexample,this:2015-04-1213:25willbeconvertedtothis:2015-04-12+13%3A25sothatitcanbetransmittedcorrectlyviaHTTP.

Yourbrowserdoesallthisautomatically,andatthemomentwearejusttestingfunctionality.Soonwewilladduserinterfaceelementstomaketheprocessofcreatingtimeanddaterangesmucheasier.

Anotherthingtoconsideristhetimezoneinwhichyouarelocated.Bydefault,theoperatingsystemissettoUTC,orCoordinatedUniversalTime.Toseewhatyourtimesettingsaretrythe“date”command:>date

TueApr1404:48:56BST2015

Page 87: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Apparently,althoughIliveinSydney,Australia,thedefaultsettingformybrand-newRasbianinstallationisBST(BritishSummerTime).

Don’tworryaboutthetimezoneissuesatthemoment,wewillspendtimeadjustingourapplicationforthatlater.

Step9.8IfthetimeanddatestringsthatwetypedintheURLarenotcorrect(example:http://192.168.111.63/lab_env_db?from=2015-04-13&to=2015—14),theSQLquerywillnotreturnanresults.Itwilllooklikethis:

Ifyouwouldratherhaveitretrieveapresetdaterange,thenweneedtoaddsomevalidationtothelab_env_dbmethod.

Goinlab_env_dbandeditthemethodtolooklikethis(thisislab_app_v4.pyonGithub):@app.route(“/lab_env_db”,methods=[‘GET’])

deflab_env_db():

Page 88: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

importdatetime

from_date_str=request.args.get(‘from’,time.strftime(“%Y-%m-%d00:00”))#Getthefromdate

valuefromtheURL

to_date_str=request.args.get(‘to’,time.strftime(“%Y-%m-%d%H:%M”))#Getthetodatevalue

fromtheURL

ifnotvalidate_date(from_date_str):#ValidatedatebeforesendingittotheDB

from_date_str=time.strftime(“%Y-%m-%d00:00”)

ifnotvalidate_date(to_date_str):

to_date_str=time.strftime(“%Y-%m-%d%H:%M”)#ValidatedatebeforesendingittotheDB

importsqlite3

conn=sqlite3.connect(‘/var/www/lab_app/lab_app.db’)

curs=conn.cursor()

curs.execute(“SELECT*FROMtemperaturesWHERErDateTimeBETWEEN?AND?”,(from_date_str,

to_date_str))

temperatures=curs.fetchall()

curs.execute(“SELECT*FROMhumiditiesWHERErDateTimeBETWEEN?AND?”,(from_date_str,

to_date_str))

humidities=curs.fetchall()

conn.close()

returnrender_template(“lab_env_db.html”,temp=temperatures,hum=humidities)

defvalidate_date(d):

try:

datetime.datetime.strptime(d,‘%Y-%m-%d%H:%M’)

returnTrue

exceptValueError:

returnFalse

Ihavehighlightedthecodethatdoesthedatevalidationinbold.Thereisanewmethod,called“validate_date”,thatacceptsastring.Itwilltrytomatchthisstringagainstatextpattern,andifitismatchedthemethodwillreturntrue,otherwiseitwillreturnfalse.The“try-except”blockistheretocatchanexception,whichisaconditionthathappenswith,inthiscase,itisnotpossibletomatchthedatestringwiththepatternwehaveprovidedtothestrptimemethod.

RestartuWSGI,andrefreshyourbrowser(whichstillcontainstheincorrectURLfromthepreviousstep,http://192.168.111.63/lab_env_db?from=2015-04-13&to=2015—14).Thisiswhatyoushouldbeseeingnow:

Page 89: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section
Page 90: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step9.9Beforegoinganyfurther,let’stidyup.Thelab_env_dbmethodhasgrownabit,anditwillcontinuetogrow.Itisbettertosimplifyitatthispointbeforeweaddmorecodetoit.

AbigpartofwhatishappeninginsidethismethodisthedecodingoftheURLquerystring.Let’ssplitthatpartintoitsownmethod.Here’sistheendresult:@app.route(“/lab_env_db”,methods=[‘GET’])

deflab_env_db():

temperatures,humidities,from_date_str,to_date_str=get_records()

returnrender_template(“lab_env_db.html”,temp=temperatures,hum=humidities)

defget_records():

from_date_str=request.args.get(‘from’,time.strftime(“%Y-%m-%d00:00”))#Getthefromdate

valuefromtheURL

to_date_str=request.args.get(‘to’,time.strftime(“%Y-%m-%d%H:%M”))#Getthetodatevalue

fromtheURL

ifnotvalidate_date(from_date_str):#ValidatedatebeforesendingittotheDB

from_date_str=time.strftime(“%Y-%m-%d00:00”)

ifnotvalidate_date(to_date_str):

to_date_str=time.strftime(“%Y-%m-%d%H:%M”)#ValidatedatebeforesendingittotheDB

importsqlite3

conn=sqlite3.connect(‘/var/www/lab_app/lab_app.db’)

curs=conn.cursor()

curs.execute(“SELECT*FROMtemperaturesWHERErDateTimeBETWEEN?AND?”,(from_date_str,

to_date_str))

temperatures=curs.fetchall()

curs.execute(“SELECT*FROMhumiditiesWHERErDateTimeBETWEEN?AND?”,(from_date_str,

to_date_str))

humidities=curs.fetchall()

conn.close()

return[temperatures,humidities,from_date_str,to_date_str]

Thenewmethod,get_records,inheritsthecodethatdidthedecodingoftheURLandthatworkswiththedatabase.Itreturnsthetwocollectionsandthedatestringsinanarray,sothatwecandomultipleassignmentinthefirstlineofthelab_env_dbmethodtolocalvariable.

Savetheupdatedscript,restartuWSGIandrefreshyourwebpage.Itshouldreloadasnothinghaschanged!

Step9.10Whatwehavenowisnice,butnotuserfriendly.Let’saddsomeUIelementssothatwecanjustclickonabuttonandretrieverecordsfromapredefinedtimerange.Forexample,wecancreatebuttonsthatretrieverecordsfromthepast3,6,12or24hourswithasingleclick.

Todothis,wewillneedtoworkwiththeHTMLtemplateandthePythonscript.Let’sstartwiththeHTMLtemplate.Openlab_env_db.htmlinyoureditor(thefileislocatedinsidethetemplatesdirectory),andupdatethecontentswiththis(thisisnamedlab_env_db_v2.htmlinGithub):<!DOCTYPEhtml>

<htmllang=“en”>

Page 91: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

<head>

<!—BasicPageNeeds

––––––––––––––––——>

<metacharset=“utf-8”>

<title>LabConditionsbyRPi</title>

<metaname=“description”content=“Labconditions-RPi”>

<metaname=“author”content=“PeterDalmaris”>

<!—MobileSpecificMetas

––––––––––––––––——>

<metaname=“viewport”content=“width=device-width,initial-scale=1”>

<!—FONT

––––––––––––––––——>

<linkhref=”//fonts.googleapis.com/css?family=Raleway:400,300,600”rel=“stylesheet”

type=“text/css”>

<!—CSS

––––––––––––––––——>

<linkrel=“stylesheet”href=”/static/css/normalize.css”>

<linkrel=“stylesheet”href=”/static/css/skeleton.css”>

<!—Favicon

––––––––––––––––——>

<linkrel=“icon”type=“image/png”href=”/static/images/favicon.png”>

</head>

<body>

<divclass=“container”>

<divclass=“row”>

<divclass=“elevencolumns”>

<formid=“range_select”action=“/lab_env_db”method=“GET”>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“3”id=“radio_3”/><label

for=“radio_3”>3hrs</label>

</div>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“6”id=“radio_6”/><label

for=“radio_6”>6hrs</label>

</div>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“12”id=“radio_12”/><label

for=“radio_12”>12hrs</label>

</div>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“24”id=“radio_24”/><label

for=“radio_24”>24hrs</label>

</div>

</form>

</div>

</div>

<divclass=“row”>

<divclass=“one-thirdcolumn”style=“margin-top:5%”>

<strong>Showingallrecords</strong>

<h2>Temperatures</h2>

<tableclass=“u-full-width”>

<thead>

<tr>

<th>Date</th>

<th>&deg;C</th>

Page 92: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

</tr>

</thead>

<tbody>

{%forrowintemp%}

<tr>

<td>{{row[0]}}</td>

<td>{{‘%0.2f’|format(row[2])}}</td>

</tr>

{%endfor%}

</tbody>

</table>

<h2>Humidities</h2>

<tableclass=“u-full-width”>

<thead>

<tr>

<th>Date</th>

<th>%</th>

</tr>

</thead>

<tbody>

{%forrowinhum%}

<tr>

<td>{{row[0]}}</td>

<td>{{‘%0.2f’|format(row[2])}}</td>

</tr>

{%endfor%}

</tbody>

</table>

</div>

</div>

</body>

<scriptsrc=”//code.jquery.com/jquery-1.11.2.min.js”></script>

<scriptsrc=”//code.jquery.com/jquery-migrate-1.2.1.min.js”></script>

<script>

jQuery(“#range_selectinput[type=radio]”).click(function(){

jQuery(“#range_select”).submit();

});

</script>

</html>

Thenewcodeishighlightedinboldletters.Thefirstblockisaformthatcontainsalistofradiobuttons.Eachbuttonrepresentsablockoftimethattheusercanselect.

Sincewewanttheuseronsimplyclickononeofthebuttonsandhavetheformsubmitted,IhaveinsertedsomeJavascriptcode,usingjQuery,tomakethiseasy.ThesecondhighlightedblockloadsthejQuerylibraryfromthejquery.comrepositoryandthensubmitstheformwhenoneoftheradiobuttonsisclicked.

Updatethecode,savethefileandreloadthepageinyourwebbrowser.Youshouldseethis:

Page 93: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Noticethe4buttonsatthetopofthepage.Ifyouclickanyofthem,thepagewillbehaveasifitisreloading.WhathappenedisthatthejQuerystatementatthebottomoftheHTMLcodedetectedtheclickandsubmittedtheform.

HavealookattheURL.Youwillseesomethinglikethis:http://192.168.111.63/lab_env_db?range_h=6

Noticetherange_hvalue?Inmycaseitcarriesthevalue“6”becauseIclickontheradiobutton“6hrs”.InowneedtoupdatemycodeinthePythonfilesothatitcanreadthisvalueandcalculatethestartandenddate/timesforthistimerange.

Step9.11Openlab_app.pyinyoureditor.Updatetheget_recordsmethodwiththiscode:defget_records():

from_date_str=request.args.get(‘from’,time.strftime(“%Y-%m-%d00:00”))#Getthefromdate

valuefromtheURL

to_date_str=request.args.get(‘to’,time.strftime(“%Y-%m-%d%H:%M”))#Getthetodatevalue

Page 94: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

fromtheURL

range_h_form=request.args.get(‘range_h’,”);#Thiswillreturnastring,iffieldrange_h

existsintherequest

range_h_int=“nan”#initialisethisvariablewithnotanumber

try:

range_h_int=int(range_h_form)

except:

print“range_h_formnotanumber”

ifnotvalidate_date(from_date_str):#ValidatedatebeforesendingittotheDB

from_date_str=time.strftime(“%Y-%m-%d00:00”)

ifnotvalidate_date(to_date_str):

to_date_str=time.strftime(“%Y-%m-%d%H:%M”)#ValidatedatebeforesendingittotheDB

#Ifrange_hisdefined,wedon’tneedthefromandtotimes

ifisinstance(range_h_int,int):

time_now=datetime.datetime.now()

time_from=time_now-datetime.timedelta(hours=range_h_int)

time_to=time_now

from_date_str=time_from.strftime(“%Y-%m-%d%H:%M”)

to_date_str=time_to.strftime(“%Y-%m-%d%H:%M”)

importsqlite3

conn=sqlite3.connect(‘/var/www/lab_app/lab_app.db’)

curs=conn.cursor()

curs.execute(“SELECT*FROMtemperaturesWHERErDateTimeBETWEEN?AND?”,(from_date_str,

to_date_str))

temperatures=curs.fetchall()

curs.execute(“SELECT*FROMhumiditiesWHERErDateTimeBETWEEN?AND?”,(from_date_str,

to_date_str))

humidities=curs.fetchall()

conn.close()

return[temperatures,humidities,from_date_str,to_date_str]

Thenewcode(inboldletters)retrievesthehoursrangefromtheURL.Becauseitistransmittedtotheserverasacharacter,wefirstconvertittoaninteger.Then,fromthisintegerwecalculatethedaterangevariables,andfinallywepassthemtotheSQLquery.

Tryout.SavethefileandrestartuWSGI.Thenreloadthepage,andclickonthe3hrsbutton.Youwillgetrecordsthatwerecreatedinthelastthreehours:

Page 95: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Wewillbedoingmoreworkwiththeuserinterfacelater.Fornow,wehaveawaytorecordsensorreadingsevery10minutes,andretrievethosereadingsusingasimpleradio-buttonbaseddate/timerangemechanism.

Beforeaddingmoreformwidgets,let’saddtheabilitytovisualiseourdata.Wewilldothisinthenextsection.

Page 96: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERTWELVEAddVisualisations

Ithinkitwouldbenicetoconvertoursensordataintogood-lookingcharts.GoogleoffersanAPIforcreatingcharts,calledGoogleChartAPI.HereIwillshowyouhowtouseittoconvertthedatafromyoursensorintoachartthatisembeddedinyourexistingpage.

TheGoogleChartAPIisextensive.Youcancreateamazingvisualisationswithit.Allthedetailsaredocumentedhere:https://developers.google.com/chart/.

Fornow,I’dliketoshowyouhowtocreatesomethinglikethis:

Tomakeitpossible,wewillworkexclusivelyontheclientside,theHTMLpage.

Page 97: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step10.1Let’sstartbyaddingHTMLcodetocreateaplaceholderforthechartsinthepage.Wewillcreateoneplaceholderforthetemperaturechart,andoneforthehumidity.Bothwillbeplacedattherightsideofthepage,likeintheexamplescreenshotabove.

Openlab_env_db.htmlinyoureditor(insidethetemplatesdirectory).Justbeforethe</div>tagabove</body>,insertthiscode(allchangesandadditionsareinafiletitled“lab_env_db_v3.html”onGithub):

<divclass=“two-thirdscolumn”style=“margin-top:5%”>

<divclass=“row”>

<divclass=“row”>

<divclass=“threecolumns”>

<divid=“chart_temps”></div>

<divid=“chart_humid”></div>

</div>

</div>

</div>

</div>

</div>

</body>

Thiscreatestwodivboxes,onewithid“chart_temps”andonewithid“chart_humid”.ThisiswherethetwochartswillbeplacedbytheGoogleChartsAPI.

Step10.2Justbeforethe</html>tagattheveryendofthefile,addthisJavascriptcode:<scripttype=“text/javascript”src=“https://www.google.com/jsapi?autoload={‘modules’:

[{‘name’:‘visualization’,‘version’:‘1’,‘packages’:[‘corechart’]}]}”></script>

<script>

google.load(‘visualization’,‘1’,{packages:[‘corechart’]});

google.setOnLoadCallback(drawChart);

functiondrawChart(){

vardata=newgoogle.visualization.DataTable();

data.addColumn(‘datetime’,‘Time’);

data.addColumn(‘number’,‘Temperature’);

data.addRows([

{%forrowintemp%}

[newDate({{row[0][0:4]}},{{row[0][5:7]}},{{row[0][8:10]}},{{row[0][11:13]}},{{row[0][14:16]}}),

{{‘%0.2f’|format(row[2])}}],

{%endfor%}

]);

varoptions={

width:600,

height:563,

hAxis:{

title:“Date”,

gridlines:{count:{{temp_items}},color:‘#CCC’},

format:‘dd-MMM-yyyyHH:mm’},

vAxis:{

title:‘Degrees’

Page 98: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

},

title:‘Temperature’,

curveType:‘function’//Makeslinecurved

};

varchart=newgoogle.visualization.LineChart(document.getElementById(‘chart_temps’));

chart.draw(data,options);

}

</script>

<script>

google.load(‘visualization’,‘1’,{packages:[‘corechart’]});

google.setOnLoadCallback(drawChart);

functiondrawChart(){

vardata=newgoogle.visualization.DataTable();

data.addColumn(‘datetime’,‘Time’);

data.addColumn(‘number’,‘Humidity’);

data.addRows([

{%forrowinhum%}

[newDate({{row[0][0:4]}},{{row[0][5:7]}},{{row[0][8:10]}},{{row[0][11:13]}},{{row[0][14:16]}}),

{{‘%0.2f’|format(row[2])}}],

{%endfor%}

]);

varoptions={

width:600,

height:563,

hAxis:{

title:“Date”,

gridlines:{count:{{hum_items}},color:‘#CCC’},

format:‘dd-MMM-yyyyHH:mm’},

vAxis:{

title:‘Percent’

},

title:‘Humidity’,

curveType:‘function’//Makeslinecurved

};

varchart=newgoogle.visualization.LineChart(document.getElementById(‘chart_humid’));

chart.draw(data,options);

}

</script>

</html>

Page 99: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step10.3WeneedtopassthetotalnumberofrecordsinthehumiditiesandtemperaturescollectionsbecausetheGoogleChartsAPIneedsthisinformationtoconstructthegridforthecharts.

Openlab_env_db.pyinyoureditor,andadjustthelab_env_dbmethodtobelikethis(titled“lab_app_v7.py”onGithub):@app.route(“/lab_env_db”,methods=[‘GET’])

deflab_env_db():

temperatures,humidities,from_date_str,to_date_str=get_records()

returnrender_template(“lab_env_db.html”,temp=temperatures,hum=humidities,temp_items=

len(temperatures),hum_items=len(humidities))

Theadditionsarehighlightedinbold.Wesimplyusethe“len”methodtocounthowmanyitemswehaveinthetwocollections,andpassthisintegertothetemplate.

Step10.4Unlessthere’satypo,thisshouldwork.SavetheupdatedPythonscriptandrestartuWSGI.

Reloadyourbrowser,andmarvelatyournewcharts!

Page 100: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

IfyouarenotfamiliarwithJavascript,itisworththeeffortofstudyingthiscodeinsomedetail.Essentially,foreachchart,weareconstructingadatatablethatcontains

Page 101: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

informationabouttheaxisandthedataforeachrow.Foreachrow,weplotthevalue,roundedtotwodecimals,againstatimestamp.Thisway,weareconstructingadateandtimechart.ThisisdocumentedintheChartDocumentation,whichIstronglyrecommendyouspendsometimewith.Inthefollowingsections,wewillimprovetheuserexperiencebyaddingelementstoallowtheusertoselectanarbitrarydaterange,addresstheissueofthetimezone,andmakeitpossibletosendthecurrentsensordatasettoPlotly,anamazingcloudvisualisationandanalysisservice,whereyoucandothingslikethis:

Page 102: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERTHIRTEENImprovetheUI

Inthissectionwewillimprovetheexistinguserinterfacebyaddingwidgetsthatallowtheusertoeasilyselectdate/timerangesforthecharts.

Todothis,IwillintroduceJQueryandtheDateTimePickerwidget.

JQueryisoneofthemostwidelyusedJavascriptframeworks.JQuerymakesJavascripteasytousebyprovidingalibraryofusefulfunctionsandvisualelements.Iwillshowyousomeofthesefeaturesaswegothroughthecontentofthissection.

JQueryisatechnologythatrunsontheclientside(browser).Therefore,mostoftheworkwewillbedoinginthissectionwillbedoneonthetemplatefiles.

Step11.1Let’smeetJQueryandtheDateTimePickerwidget.Hereisjquery.comfromwhereyoucandownloadorfindtheCDNlinkforthelibraryfile:

ThereareseveralwidgetsthatallowdateandtimeselectionthatworkwithJQuery.The

Page 103: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

onethatIprefertouseinthisprojectisdatetimepickerbecauseitisveryconfigurableandlooksgreat:

Step11.2TomakejQueryandthewidgetavailabletoourtemplate,weneedtoincludethemintoourpage.ForjQuery,theeasiestwaytousetheCDN(ContentDeliveryNetwork)link.Thisway,ourtemplatewillalwaysbeusingtheofficiallibrarymaintainedbythemaintainersofjQuery.Thiswasalreadydoneintheprevioussections,sothereisnomoreworktodo.

Page 104: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Toaddthedatetimewidget,downloadtheZIPfilefromhttp://xdsoft.net/jqplugins/datetimepicker/.ACDNisnotsupported.

Takethe.cssfileandcopyitintothestatic/cssfolder,andthe.jsfileintothestatic/javascriptfolder.Ifthejavascriptfolderdoesn’texist,createit.

Yourstaticfoldershouldlooklikethisnow:

Page 105: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step11.3Openthelab_env_db.htmltemplatefileandaddthesetwolinesrightunderthereferencetothejquery-1.11.2.min.jsfile(thisversionofthefileistitled“lab_env_db_v4.html”onGithub):

<scriptsrc=”//code.jquery.com/jquery-1.11.2.min.js”></script>

<linkrel=“stylesheet”type=“text/css”href=”/static/css/jquery.datetimepicker.css”/>

<scriptsrc=”/static/javascript/jquery.datetimepicker.js”></script>

Page 106: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step11.4Next,wewilldefinetwoinputfieldsinwhichwewillplacethetwodatetimepickerwidgets.

Let’saddanewCSSrowthatcontainsanewform.Withinthatform,we’llhavetwoinputsthatwillserveasdatetimepickers.Clickingonabuttonwillsubmitthetwodatesselectedbytheuser.

Copythiscodeimmediatelyunderthe<body>tag:<divclass=“container”>

<divclass=“row”>

<formid=“datetime_range”action=”/lab_env_db”method=“GET”>

<!—<divclass=“row”>—>

<divclass=“threecolumns”>

<labelfor=“from”>Fromdate</label>

<inputclass=“u-full-width”id=“datetimepicker1”type=“text”value=”{{from_date}}”name=“from”>

</div>

<!—</div>—>

<!—<divclass=“row”>—>

<divclass=“threecolumns”>

<labelfor=“to”>Todate</label>

<inputclass=“u-full-width”id=“datetimepicker2”type=“text”value=”{{to_date}}”name=“to”>

</div>

<!—</div>—>

<!—<divclass=“row”>—>

<divclass=“twocolumns”>

<inputclass=“button-primary”type=“submit”value=“Submit”style=“position:relative;top:28px”

id=“submit_button”/>

</div>

<!—</div>—>

</form>

</div>

Thetwoinputsarehighlightedbold.

NoticethatthedefaultdateandtimestobedisplayedineachfieldareprovidedbytheFlashapplicationviathe“from_date”and“to_date”variables.WewillneedtoadjustourPythoncodetoincludethesevariablesintheparametersthatarepassedtothetemplate.

Page 107: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step11.5Scrolltowardsthebottomofthelab_env_db.htmltemplatefile,towheretheJavascriptcodeis.

Undertheinclusionofthedatetimepickerlibrary,pastethiscode:<script>

jQuery(‘#datetimepicker1’).datetimepicker(

{

format:‘Y-m-dH:i’,

defaultDate:’{{from_date}}’

});

jQuery(‘#datetimepicker2’).datetimepicker({

format:‘Y-m-dH:i’,

defaultDate:’{{to_date}}’

});

jQuery(“#range_selectinput[type=radio]”).click(function(){

jQuery(“#range_select”).submit();

});

</script>

Thiscodewillplacethedatetimepickerstotheinputfieldswithnamesdatetimepicker1anddatetimepicker2,andconfigurethewaytheyshoulddisplaytheirvalues.HereistheGistforthisfileandversion:http://txplo.re/1klcHDS

Step11.6Let’sswitchtothePythonscript.Openlab_app.pyinyoureditor.Gotothelab_env_dbfunction,andchangethereturnstatementtothis:@app.route(“/lab_env_db”,methods=[‘GET’])

deflab_env_db():

temperatures,humidities,from_date_str,to_date_str=get_records()

returnrender_template(“lab_env_db.html”,temp=temperatures,

hum=humidities,

from_date=from_date_str,

to_date=to_date_str,

temp_items=len(temperatures),

hum_items=len(humidities))

Thenewtextisinbold.TheGistforthisfileandversionis:http://txplo.re/1l2EY2v

Page 108: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step11.7Toseethechanges,restartuwsgiandrefreshthepage:>restartuwsgi

uwsgistart/running,process13058

Trychangingthedateandtimerange,andclickingSubmit.Confirmthattherecordsretrievedareactuallywithintherangeyouselected.

Itisnowgettingeasiertointeractwiththedatabase.

Thereisapendingissuethough:thedatesandtimesasrecordedontheRPiserverareinwhichevertimezonetheserverissetfor.Usually,thisisUTC.Ifyouareatadifferenttimezone,thetimedisplayedintheGooglechartandintherecordstablewillnotmatchtheactualtimethattherecordwascreated,butitwillbeoffset.

Let’scorrectthisprobleminthenextsection.

Page 109: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERFOURTEENAdjustingforactualtimezone

Itwouldbereallynicefortheapplicationtobeabletodetecttheuser’stimezonebasedontheinformationprovidedbythebrowser,andthentoautomaticallyadjustthetimesofeachrecordforthattimezone.Thisway,timesshownintherecordstableandintheGooglechartwillbecorrectfortheuser’stimezone.

Sotherearetwocomponentsforourtimezonesolution:client(Javascript)andserver(Python).

Step12.1Wewillstartwiththeclient.Wewanttodetecttheuser’stimezonebygettinginformationfromtheirbrowser.WecanthenpassthisinformationtotheserverbypiggybackingitinaformGETrequest.

ThereisaveryusefulJavascriptlibrarythatcanhelpusgetthetimezoneinformation:https://bitbucket.org/pellepim/jstimezonedetect

ThislibraryisavailableviaCDN,sothereisnothingtodownloadtotheRPi.Openlab_env_db.htmlinyoureditorandaddthislineunderthepreviousJavascriptinclusions:<scriptsrc=“https://cdnjs.cloudflare.com/ajax/libs/jstimezonedetect/1.0.4/jstz.min.js”></script>

Step12.2Wewilltransmitthetimezoneinformationtotheserverwiththeuseofhiddenfields.Sincewehavetoforms,wewilladdatimezonehiddenfieldtoeachone.Thetimezoneinformationwilltraveltotheserverwhentheusersubmiteitherform.

Aslongasthehiddenformiswithintheformtags,it’sexactpositiondoesnotmatter.Iusuallyplacethemrightbeforethesubmitbuttonorrightafterthe<form>tagifthereisnobutton(thesecondformonlyhadradiobuttons).

Herearethetwoforms,withthehiddenfieldsinbold(theupdatedfileforthislectureistitled“lab_env_db_v5.htmlonGithub):<divclass=“row”>

<formid=“datetime_range”action=”/lab_env_db”method=“GET”>

<!—<divclass=“row”>—>

<divclass=“threecolumns”>

<labelfor=“from”>Fromdate</label>

<inputclass=“u-full-width”id=“datetimepicker1”type=“text”value=”{{from_date}}”name=“from”>

</div>

<!—</div>—>

<!—<divclass=“row”>—>

<divclass=“threecolumns”>

<labelfor=“to”>Todate</label>

<inputclass=“u-full-width”id=“datetimepicker2”type=“text”value=”{{to_date}}”name=“to”>

</div>

Page 110: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

<!—</div>—>

<!—<divclass=“row”>—>

<divclass=“twocolumns”>

<inputtype=“hidden”class=“timezone”name=“timezone”/>

<inputclass=“button-primary”type=“submit”value=“Submit”style=“position:relative;top:28px”

id=“submit_button”/>

</div>

<!—</div>—>

</form>

</div>

<divclass=“row”>

<divclass=“elevencolumns”>

<formid=“range_select”action=“/lab_env_db”method=“GET”>

<inputtype=“hidden”class=“timezone”name=“timezone”/>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“3”id=“radio_3”/><labelfor=“radio_3”>3hrs</label>

</div>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“6”id=“radio_6”/><labelfor=“radio_6”>6hrs</label>

</div>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“12”id=“radio_12”/><labelfor=“radio_12”>12hrs</label>

</div>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“24”id=“radio_24”/><labelfor=“radio_24”>24hrs</label>

</div>

</form>

</div>

</div>

Page 111: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step12.3Beforeeachformissubmitted,thepagemustfirstgetthetimezonefortheuserandputthevalueinthehiddenfields.WewillusejQueryforthis.Hereistheexamplecode:timezone=jstz.determine();

jQuery(“.timezone”).val(timezone.name());

Wegetthetimezonewithjstsz.determine()andcopythevaluetothehiddenfieldwiththeid“timezone”inthesecondline.

Wewantthiscodetobeexecutedwiththerealisationofaformsubmitevent.Forthedatetimepickerform,thecodeisthis:jQuery(“#datetime_range”).submit(function(event){

timezone=jstz.determine();

jQuery(“.timezone”).val(timezone.name());

});

Fortheradiobuttonsform,itlookslikethis:jQuery(“#range_selectinput[type=radio]”).click(function(){

timezone=jstz.determine();

jQuery(“.timezone”).val(timezone.name());

jQuery(“#range_select”).submit();

});

CompletefileGistforthisversion:http://txplo.re/1N6luAG

Step12.4Uploadthenewtemplatecodetotheserverifyouhaven’tdonesoalready.

Page 112: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step12.5InPython,wewilldotimeanddatecalculationsusingArrow,apopularpackageforjustthispurpose.

Let’sinstallitinourapp’svirtualenvironment:>/var/www/lab_app/venv/bin/pipinstallarrow

Downloading/unpackingarrow

Downloadingarrow-0.5.4.tar.gz(81Kb):81Kbdownloaded

Runningsetup.pyegg_infoforpackagearrow

Downloading/unpackingpython-dateutil(fromarrow)

Downloadingpython-dateutil-2.4.2.tar.gz(209Kb):209Kbdownloaded

Runningsetup.pyegg_infoforpackagepython-dateutil

Downloading/unpackingsix>=1.5(frompython-dateutil->arrow)

Downloadingsix-1.9.0.tar.gz

Runningsetup.pyegg_infoforpackagesix

nopreviously-includeddirectoriesfoundmatching‘documentation/_build’

Installingcollectedpackages:arrow,python-dateutil,six

Runningsetup.pyinstallforarrow

Runningsetup.pyinstallforpython-dateutil

Runningsetup.pyinstallforsix

nopreviously-includeddirectoriesfoundmatching‘documentation/_build’

Successfullyinstalledarrowpython-dateutilsix

Cleaningup…

Step12.6Openlab_app.pyinyoureditor.Startbyimportingthearrowlibrarytothescriptatthetopofthefile:importarrow

Therestofthechangesareinmethodslab_env_dbandget_records.Youshouldspendabitoftimelookingatthecodeinthesemethodsinordertounderstandhowtimezoneinformationisusedtoconverttimefromonetimezonetotheother.

Afewthingstoremember:

Ifatimezoneisnotdefined,thenweuseEtc/UTCasthedefault.Timesusedinthedatabaserecordsareinthetimezoneoftheserver(RPi).ItisagoodpracticetoalwaysadjustyourserversothatitstimezoneisEtc/UTC.Youcanuse“dpkg-reconfiguretzdata”tomakechanges.

Hereisthecodeforlab_env_db()(theupdatedfileforthislectureistitled“lab_app_v9.py”onGithub):@app.route(“/lab_env_db”,methods=[‘GET’])#AdddatelimitsintheURL#Arguments:from=2015-03-

04&to=2015-03-05

deflab_env_db():

temperatures,humidities,timezone,from_date_str,to_date_str=get_records()

#Createnewrecordtablessothatdatetimesareadjustedbacktotheuserbrowser’stimezone.

time_adjusted_temperatures=[]

time_adjusted_humidities=[]

forrecordintemperatures:

Page 113: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

local_timedate=arrow.get(record[0],“YYYY-MM-DDHH:mm”).to(timezone)

time_adjusted_temperatures.append([local_timedate.format(‘YYYY-MM-DDHH:mm’),

round(record[2],2)])

forrecordinhumidities:

local_timedate=arrow.get(record[0],“YYYY-MM-DDHH:mm”).to(timezone)

time_adjusted_humidities.append([local_timedate.format(‘YYYY-MM-DDHH:mm’),round(record[2],2)])

print“renderinglab_env_db.htmlwith:%s,%s,%s”%(timezone,from_date_str,to_date_str)

returnrender_template(“lab_env_db.html”,timezone=timezone,

temp=time_adjusted_temperatures,

hum=time_adjusted_humidities,

from_date=from_date_str,

to_date=to_date_str,

temp_items=len(temperatures),

hum_items=len(humidities))

Thecodeforget_records()isthis:defget_records():

from_date_str=request.args.get(‘from’,time.strftime(“%Y-%m-%d00:00”))#Getthefromdate

valuefromtheURL

to_date_str=request.args.get(‘to’,time.strftime(“%Y-%m-%d%H:%M”))#Getthetodatevalue

fromtheURL

timezone=request.args.get(‘timezone’,‘Etc/UTC’);

range_h_form=request.args.get(‘range_h’,”);#Thiswillreturnastring,iffieldrange_h

existsintherequest

range_h_int=“nan”#initialisethisvariablewithnotanumber

print“REQUEST:”

printrequest.args

try:

range_h_int=int(range_h_form)

except:

print“range_h_formnotanumber”

print“Receivedfrombrowser:%s,%s,%s,%s”%(from_date_str,to_date_str,timezone,

range_h_int)

ifnotvalidate_date(from_date_str):#ValidatedatebeforesendingittotheDB

from_date_str=time.strftime(“%Y-%m-%d00:00”)

ifnotvalidate_date(to_date_str):

to_date_str=time.strftime(“%Y-%m-%d%H:%M”)#ValidatedatebeforesendingittotheDB

print‘2.From:%s,to:%s,timezone:%s’%(from_date_str,to_date_str,timezone)

#CreatedatetimeobjectsothatwecanconverttoUTCfromthebrowser’slocaltime

from_date_obj=datetime.datetime.strptime(from_date_str,’%Y-%m-%d%H:%M’)

to_date_obj=datetime.datetime.strptime(to_date_str,’%Y-%m-%d%H:%M’)

#Ifrange_hisdefined,wedon’tneedthefromandtotimes

ifisinstance(range_h_int,int):

arrow_time_from=arrow.utcnow().replace(hours=-range_h_int)

arrow_time_to=arrow.utcnow()

from_date_utc=arrow_time_from.strftime(“%Y-%m-%d%H:%M”)

to_date_utc=arrow_time_to.strftime(“%Y-%m-%d%H:%M”)

from_date_str=arrow_time_from.to(timezone).strftime(“%Y-%m-%d%H:%M”)

to_date_str=arrow_time_to.to(timezone).strftime(“%Y-%m-%d%H:%M”)

else:

#ConvertdatetimestoUTCsowecanretrievetheappropriaterecordsfromthedatabase

from_date_utc=arrow.get(from_date_obj,timezone).to(‘Etc/UTC’).strftime(“%Y-%m-%d%H:%M”)

to_date_utc=arrow.get(to_date_obj,timezone).to(‘Etc/UTC’).strftime(“%Y-%m-%d%H:%M”)

conn=sqlite3.connect(‘/var/www/smart/temp_hum2.db’)

curs=conn.cursor()

Page 114: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

curs.execute(“SELECT*FROMtemperaturesWHERErDateTimeBETWEEN?AND?”,

(from_date_utc.format(‘YYYY-MM-DDHH:mm’),to_date_utc.format(‘YYYY-MM-DDHH:mm’)))

temperatures=curs.fetchall()

curs.execute(“SELECT*FROMhumiditiesWHERErDateTimeBETWEEN?AND?”,

(from_date_utc.format(‘YYYY-MM-DDHH:mm’),to_date_utc.format(‘YYYY-MM-DDHH:mm’)))

humidities=curs.fetchall()

conn.close()

return[temperatures,humidities,timezone,from_date_str,to_date_str]

HereisthecompleteappscriptGistforthisversion:http://txplo.re/1WsscWE

Step12.7Noticethatinthelab_env_dbmethod,wehavecreatedtwonewarrays.Inthosearrayswecopythedatafromthosethatareretrievedfromthedatabase,butwiththeirtimesadjustedfortheuser’slocaltimezone.Thesearraysonlycontain2columns,asopposedtothethreecolumnsthatcomeoutofthedatabase;weonlyneedthetime/dateandvalue.So,weneedtomakeasmallchangeinthetemplatefiletoaccommodateforthischange.

Openlab_env_db.htmlandsearchforthisstring:{{‘%0.2f’|format(row[2])}}

Changebothinstancesittothis:{{‘%0.2f’|format(row[1])}}

AlsomakethesamechangetotheJavascriptcodefortheGoogleChartsAPI.Searchforformat(row[2])

andchangebothinstancesto:format(row[1])

HereisthecompletetemplateGistforthisversion:http://txplo.re/1LL1cyR

Step12.8UploadandsavethenewcodetoyourRPi,andrestartuWSGI.

Ifyourpageisnotloading(“502BadGateway”),checktheerrorlogs:>tail/var/log/uwsgi/lab_app_uwsgi.log

Page 115: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step12.9Refreshyourbrowser.IfyouareusingGoogleChrome,turnonthedevelopertoolssoyoucanseethecontentsoftheGETrequestwhenyousubmitoneoftheforms.

ForexampleifIclickonthe6hrbutton,Igetthis:

ChecktheURL,andthatitcontainsavalueforthe“timezone”parameter.Youcanseethesamethinginthedevelopertoolssection:clickonNetwork,thenonthefirstrequestintheleftpaneofthewindow.TherequestURLcontainsthetimezoneinformationfromtheuser’sbrowser.

Notmuchmorelefttodo.Inthenextsection,wewilladdcodesothattheusercaneasilymovebetweenthepastrecordsandcurrentvaluespages.Andinthelastsection,wewillmakeiteasytosentourrecordstoPlotlysothatwecanusePlotly’sdataanalysistools.

Page 116: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERFIFTEENLinkthetwopages

Inthissection,wewillmakeiteasyfortheusertomovebetweenthetwopagesthatmakeupourapplication.Inthedatabaserecordspage,we’lladdalinktothecurrentconditionspage.

Andinthecurrentconditionspage,we’lladdtheradiobuttonsforthetimerangessothattheusercanquicklyretrieverecenthistory.

Step13.1Let’sstartwiththeCurrentConditionspage.Openlab_temp.htmlinyoureditor.

Thisfilecurrentlylookslikethis(Gist):http://txplo.re/1KRHw8s

Page 117: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step13.2Thetimerangeradiobuttonsformwewanttoaddinlab_temp.htmlalreadyexistsinlab_env_db.html.Youcanopenthisfileinyoureditorandfindthatcodesegment,orjustcopythefollowingtolab_temp.html(pasteitrightafterthe</div>thatclosesthe<divclass=“row”>(theupdatedfileistitled“lab_temp_v3.html”onGithub):<divclass=“row”>

<divclass=“elevencolumns”>

<formid=“range_select”action=“/lab_env_db”method=“GET”>

<inputtype=“hidden”class=“timezone”name=“timezone”/>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“3”id=“radio_3”/><labelfor=“radio_3”>3hrs</label>

</div>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“6”id=“radio_6”/><labelfor=“radio_6”>6hrs</label>

</div>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“12”id=“radio_12”/><labelfor=“radio_12”>12hrs</label>

</div>

<divclass=“onecolumn”>

<inputtype=“radio”name=“range_h”value=“24”id=“radio_24”/><labelfor=“radio_24”>24hrs</label>

</div>

</form>

</div>

RememberthatwehavesomeJavascriptcodethatdealswiththetimezones,sowewillneedtoalsocopythatacrosstolab_temp.html.Findthiscodeinlab_env_db.html,orcopythecodefrombelow(thiscodegoesrightafterthe</body>:<scriptsrc=”//code.jquery.com/jquery-1.11.2.min.js”></script>

<scriptsrc=“https://cdnjs.cloudflare.com/ajax/libs/jstimezonedetect/1.0.4/jstz.min.js”></script>

<script>

jQuery(document).ready(function(){

timezone=jstz.determine();

jQuery(“#timezone”).text(timezone.name());

});

jQuery(“#range_selectinput[type=radio]”).click(function(){

timezone=jstz.determine();

jQuery(“.timezone”).val(timezone.name());

jQuery(“#range_select”).submit();

});

</script>

ThisGistcontainsthefinalversionforlab_temp.html:http://txplo.re/1klcXmj

Step13.3Loadlab_tempinyourbrowser,andyoushouldseethis:

Page 118: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Youcanclickoneoftheradiobuttonstoseerecordsfromrecentpast:

Page 119: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Noticethatmychartherehasastrangerange,from400to-1200degreescentigrade.Obviouslythisisnotright.Whathappenedisthatthesensorproducedafalsereadingof-999.00at2015-04-2111:50,probablybecauseIwasfiddlingaroundwiththewires.

Itwouldbenicetoaddsomecodetofilteroutvalueslikethesethatareobviouslyerroneous,insteadoflettingthemthroughandcorruptingthechart.Iwillleavethatuptoyoutodoifyouwish.

Page 120: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step13.4Let’smovetolab_env_db.html.Wewilladdasimplelinkthatwecanfollowtoviewthecurrenttemperatureandhumidityinthelab.Wewilladdthislinkunderthetwoformfieldsusedbythedatetimepickerwidget.

Pastethiscoderightbeforethe<form>tagforthetimerangeradiobuttons(theupdatedfileistitled“lab_env_db_v6.html”onGithub):<divclass=“onecolumn”>

<ahref=”/lab_temp”style=“position:relative;top:15px”>Current</a>

</div>

Yourlab_env_db.htmlshouldnowbethis(Gist):http://txplo.re/1OhQ686

Page 121: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step13.5Loadlab_env_dbinyourbrowser,youshouldbeseeingthis:

Noticethe“Current”link?Ifyouclickonit,youwillreachtheCurrentConditionspage.

Interconnectingthetwopagesoftheapplicationwasaneasytaskconsideringthatwealreadyhadmostofthenecessarycode.

Inthenextandlastsectionofthisproject,wewillmakeitpossiblefordatafromourapplicationtobetransmittedtoPlotlyforanalysis.

WithPlotly,youcanvisualiseandanalyseyourdataonthecloud,andshareit,ifyouwant,withtherestoftheworld.

Page 122: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERSIXTEENUploadingtoPlotly

Inthissection,wewilluploadsensordatafromourapplicationdatabasetoPlotly.OnceyourdataisonPlotly,wewillvisualiseitandscratchthesurfaceofwhatispossibletodointermsofdataanalysis.

Step14.1StartbycreatingafreeaccountonPlotly.Gottoplotly.io,clickontheSignUpbutton,andfollowtheprocess:

Page 123: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.2PlotlyprovidesextensivedocumentationonhowtouseitwithPython.Youcanstarthere:https://plot.ly/python/getting-started/.

GoaheadandinstallthePlotlypackageforPython.Withthispackage,connectingyourPythonapplicationtoPlotlyis(almost)trivial.

FromyourapplicationdirectoryinRPi,runthePIPtooltoinstallthepackageintheapp’svirtualdirectory:root@raspberrypi:/var/www/lab_app#venv/bin/pipinstallplotly

Downloading/unpackingplotly

Downloadingplotly-1.6.16.tar.gz(103Kb):103Kbdownloaded

Runningsetup.pyegg_infoforpackageplotly

Downloading/unpackingrequests(fromplotly)

Downloadingrequests-2.6.0.tar.gz(450Kb):450Kbdownloaded

Runningsetup.pyegg_infoforpackagerequests

Requirementalreadysatisfied(use—upgradetoupgrade):sixin./venv/lib/python2.7/site-packages

(fromplotly)

Downloading/unpackingpytz(fromplotly)

Downloadingpytz-2015.2.tar.bz2(166Kb):166Kbdownloaded

Runningsetup.pyegg_infoforpackagepytz

Installingcollectedpackages:plotly,requests,pytz

Runningsetup.pyinstallforplotly

Runningsetup.pyinstallforrequests

Runningsetup.pyinstallforpytz

Successfullyinstalledplotlyrequestspytz

Cleaningup…

Page 124: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.3Let’stestthepackage.AllcommunicationbetweenyourcomputerandPlotlyissignedwithyouraccountAPI.Beforegoinganyfurther,logontoyourPlotlyaccountandgetyourAPIkey.

Tofindyours,clickonyourusernameinthetoprightcornerofthepage,then“Settings”.TheAPIkeyisinthefirstbox.Keepthiswindowopensoyoucancopythekeyandusernameinthenextstep.

Step14.4NowwecaninitialisethePlotlypackage.

Inthecodebelow,replace“DemoAccount”withyouractualaccountname,and“your_api_key”withyouractualAPIkey.Assumingyouarestillintherootdirectoryofyourapplication,enterthiscommand:>venv/bin/python-c“importplotly;plotly.tools.set_credentials_file(username=‘DemoAccount’,

api_key=‘your_api_key‘)”

Page 125: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.5YouraccountisnowregisteredwiththePlotlypackage.Let’strytocreateanewchart.WecanusethePythoncommandlineinterpreterandanexamplefromthedocumentation.

Type“venv/bin/python”inthecommandlinetoenterthecommandlineinterpreter:>venv/bin/python

Python2.7.3(default,Mar182014,05:13:23)

[GCC4.6.3]onlinux2

Type“help”,“copyright”,“credits”or“license”formoreinformation.

>>>

Step14.6Onelineatatime,copyandpastethisprogramintothecommandline:importplotly.plotlyaspy

fromplotly.graph_objsimport*

trace0=Scatter(

x=[1,2,3,4],

y=[10,15,13,17]

)

trace1=Scatter(

x=[1,2,3,4],

y=[16,5,11,9]

)

data=Data([trace0,trace1])

unique_url=py.plot(data,filename=‘basic-line’)

Theprocesslookedlikethisforme:>>>importplotly.plotlyaspy

>>>fromplotly.graph_objsimport*

>>>trace0=Scatter(

…x=[1,2,3,4],

…y=[10,15,13,17]

…)

>>>trace1=Scatter(

…x=[1,2,3,4],

…y=[16,5,11,9]

…)

>>>data=Data([trace0,trace1])

>>>unique_url=py.plot(data,filename=‘basic-line’)

/var/www/lab_app/venv/local/lib/python2.7/site-packages/requests/packages/urllib3/util/ssl_.py:79:

InsecurePlatformWarning:

AtrueSSLContextobjectisnotavailable.Thispreventsurllib3fromconfiguringSSLappropriately

andmaycausecertainSSLconnectionstofail.Formoreinformation,see

https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.

Page 126: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.7PlotlycomplainedaboutanissuewithSSL.Ifyoureceivedthiserrormessageyoucansafelyignoreitfornow.Thecharthasbeencreatedanyway.Atsomelatertime,youcanupgradeyourPythonvirtualenvironmenttoanewerversion,2.7.9orlater,wherethisissuehasbeencorrected.

WeneedtogettheURLforthenewchart.TheURLisstoredintheunique_urlvariable.

TypethisinthePythoninterpreter:>>>printunique_url

https://plot.ly/~futureshocked/8

TheURL(formychart)ishighlightedinbold.Copytoyourwebbrowser.Youshouldseesomethinglikethis:

Page 127: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.8Ok,weknowthatwecansendchartdatatoourPlotlyaccount.Let’supdateourapplicationsothatwecansendsensordataondemand,withtheclickofabutton.

Bystudyingtheexampleprogram,andlookingthroughthedocumentation,welearnthatweneedtocreateaDataobject.Init,wecreatetraceobjects.Thetraceobjectwillcontainthedatatobedisplayed,andsomeattributes,likethetextfortheaxes,thenameofthechart,andmore.

Iwouldlikemycharttobeatimeseries,sincetimeisarrangedonthehorizontalaxis.Iwouldalsoliketodisplaybothhumidityandtemperatureonasinglechart,withtheCelsiusscaleontheleftverticalaxisandtheHumidityPercentscaleontherightverticalaxis.

ThisistheresultIwouldliketoachieve:

Page 128: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.9Fromauserinterfaceperspective,Iwouldliketousertobeabletoclickonalinkinthelab_env_dbpage,andhavethedatasenttoPlotly.Whenthechartisready,IwouldlikealinktoappearunderthePlotlylinkwhichIcanclickandgotothechart.

Thisinvolvesasynchronouscallsattwolevels.First,whenIclickonthePlotlylink,anasynchronousGETrequestissenttotheapplicationontheRPirequestingthechart.

Next,thePlotlypackagemakesit’sownrequesttoPlotly,andretrievestheURLforthechart,whenthechartisready.Last,thisURLisreturnedtothebrowser,asaresponsetothefirstasynchronousrequest.

Step14.10jQuerywillmakeallthiseasy.Let’sstartwiththeHTMLtemplatepage.Openlab_env_db.htmlinyoureditor.

Addthelinkfirst.Copythiscodejustbeforethelinktothelab_temppage:<divclass=“onecolumn”>

<ahref=””id=“plotly”style=“position:relative;top:15px”>Plotly</a>

</div>

Thehrefattributeisempty,becauseitscontentdependsonthedatathatwewanttosenttoPlotly.ItwillbepopulatedbythejQuerycode(next).

Step14.11Wewillalsoaddcodeforalinkthatwillbeabletoclickwhenthechartisready.Hereisthecode:<divclass=“row”>

<ahref=””id=“plotly_url”target=“_blank”></a><spanid=“plotly_wait”></span>

</div>

Pastethiscoderightabovetherecordsrow.

Page 129: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.12Finally,thejQuery.Hereitis:

jQuery(“#plotly”).click(function(){

jQuery(“#plotly_wait”).text(“Sendingdata…”);

jQuery(“#plotly_url”).text(””);

{%autoescapefalse%}

jQuery.get(“/to_plotly?{{query_string}}”)

{%endautoescape%}

.done(function(data){

jQuery(“#plotly_url”).attr(“href”,data);

jQuery(“#plotly_url”).text(“Clicktoseeyourplot”);

jQuery(“#plotly_wait”).text(””);

});

returnfalse;//Thisissothattheclickonthelinkdoesnotcausethepagetorefresh

});

PastethisrightafterthejQuerycodefortherange_selectbuttons.ThisisthecodethatgeneratestheURLtobecalledwhenweclickonthelink.Itpointstoamethodinourapplication,whichwehavenotcreatedyet.

Thismethodconstructsthedataobjectsforthechart,andtransmitsthemtoPlotlyusingthePlotlyAPIpackage.ItthenreadstheresponsefromtheAPIthatcontainsthenewchartURLandputsthatURLinourpagesothattheusercanclickonit.Hereisthefullcodeinlab_env_db.htmlatthispoint(Gist):http://txplo.re/1N6lTTR

Step14.13Let’sswitchtolab_app.py.Hereisthecodethatmakesuptheto_plotlymethod(youcanfindthisversiononGithub,titled“lab_app_v10.py”):@app.route(“/to_plotly”,methods=[‘GET’])#Thismethodwillsendthedatatoploty.

defto_plotly():

importplotly.plotlyaspy

fromplotly.graph_objsimport*

temperatures,humidities,timezone,from_date_str,to_date_str=get_records()

#Createnewrecordtablessothatdatetimesareadjustedbacktotheuserbrowser’stimezone.

time_series_adjusted_tempreratures=[]

time_series_adjusted_humidities=[]

time_series_temprerature_values=[]

time_series_humidity_values=[]

forrecordintemperatures:

local_timedate=arrow.get(record[0],“YYYY-MM-DDHH:mm”).to(timezone)

time_series_adjusted_tempreratures.append(local_timedate.format(‘YYYY-MM-DDHH:mm’))

time_series_temprerature_values.append(round(record[2],2))

forrecordinhumidities:

local_timedate=arrow.get(record[0],“YYYY-MM-DDHH:mm”).to(timezone)

time_series_adjusted_humidities.append(local_timedate.format(‘YYYY-MM-DDHH:mm’))#Besttopass

datetimeintext

#sothatPlotlyrespectsit

time_series_humidity_values.append(round(record[2],2))

temp=Scatter(

x=time_series_adjusted_tempreratures,

Page 130: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

y=time_series_temprerature_values,

name=‘Temperature’

)

hum=Scatter(

x=time_series_adjusted_humidities,

y=time_series_humidity_values,

name=‘Humidity’,

yaxis=‘y2’

)

data=Data([temp,hum])

layout=Layout(

title=“TemperatureandhumidityinPeter’slab”,

xaxis=XAxis(

type=‘date’,

autorange=True

),

yaxis=YAxis(

title=‘Celcius’,

type=‘linear’,

autorange=True

),

yaxis2=YAxis(

title=‘Percent’,

type=‘linear’,

autorange=True,

overlaying=‘y’,

side=‘right’

)

)

fig=Figure(data=data,layout=layout)

plot_url=py.plot(fig,filename=‘lab_temp_hum’)

returnplot_url

Copythiscodesomewhereinthescript.ItwillrespondtoGETrequestsonly,constructthedataobjectaspertheparametersprovidedbytheuser,transmitthedataobjecttoPlotly,gettheURLforthenewchartfromthePlotlyresponse,andreturntheURLtothejQuerycodeinthetemplatepage.Hereisthecompletecodeforlab_app.py(Gist):http://txplo.re/1OhQbst

Page 131: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.14Let’stryoutthenewcode.RestartuWSGIandloadtherecordspage:

Page 132: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.15ClickonthePlotlylinktotriggerthechartrequest.The“Sendingdata…”messagewillappearforafewseconds,andthenitwillbereplacedby“Clicktoseeyourplot”link:

Page 133: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.16Clickonit,andmarvelonyournewchart!

Page 134: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

Step14.17Ok,mychartisnotveryglamorousbecausethatinfamousbadrecordinmydataset.ButhereisoneIcreatedearlier:

Step14.18Itworks!SowhatelsecanyoudowithchartonPlotly?Startbycheckingouttherestofthetabsinthegraphplotbox.There’sData,CodeandExtras.Then,clickon“EditGraph”fornumerousoptionsonthemesandvariousfunctions.

Forexample,clickingonTracesgivesyouaccesstooptionsthatallowyoutomanipulateeitherofthelines.Thereisalsoafunctionthatcancalculatethebestfittinglinetoatrace,whichisgoodfordetectingtrends.Plotlyisaverycapableonlineresource,worthspendingabitoftimetobecomefamiliarwith.

Page 135: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

CHAPTERSEVENTEENConclusion

Congratulations!

YouhavecompletedthisintroductoryprojectontheRaspberryPi.Youarenowfamiliarwithsomeofthethingsthatyoucandowithit,andwiththeflexibilityandpoweritofferswhencomparedtoothertechnologies,liketheArduino.

Let’srecapwhatyouhavelearned:

YouknowhowtoinstallandconfigureRasbian,HowtoinstallandusePythonandthePythonVirtualEnvironment,Youunderstandwhatisthewebapplicationstackanditscomponents,YouunderstandtheuseofnamingsystemofGPIOs,YoucaninteractwithsimpleandmorecomplicateddevicesconnectedtotheGPIOs,Youarefamiliarwithavarietyoffundamentalopensourcesoftwareandservices,likeNginx,Flask,uWSGI,Upstart,Skeleton,SQLite3,cron,Javascript,Jquery.Youhaveexperiencewithpopularwebservices,likeGoogleChartsandPlotly.And,mostimportant,youhavecreatedafull-stackapplicationonyourRaspberryPiinwhichyouappliedallthesecomponentsinasinglesystem.

What’snext?It’suptoyou!However,Icansuggestacoupleofideas.

Howaboutyouextendyourexistingapplication?Addmoresensors,addautomaticnotificationswhenthesesensorsreachatriggervalue,thinkaboutcontrollingsomething.

Howaboutworkingontheuserinterfacetomakeititmoreinteresting?Youcanexperimentwithdifferentwidgetsordatavisualisations.YoucanfigureoutawaytomakeyourapplicationwebsiteaccessiblefromtheWeb.Youcanpublishyourdatatodataloggingservices.YoucanconnectyourRaspberryPitoathermostatandgetittocontrolyourhomeenvironment!

Or,forgetabouttheapplicationyoujustcreated,andstartfromscratch.Thinkbig,orthinksmall,itisuptoyouanditdependsonwhatdrivesyouandhowyoulearn.

Butwhateveryoudecidetodo,maybeIcanhelpyou.Sendmeamessageandletmeknow.

Thiswasjustthebeginning!

Page 136: Raspberry Pi: Full Stack: A whirlwind tour of full-stack ...prophet/raspberrypi/Raspberry... · Section: 5 - Building a simple Flask application on the Raspberry Pi Lecture 28: Section

TableofContents

AboutthisbookVideocoursecompanionDiscussionforumandemaillistTomakethemostofthisbookCHAPTERONE-IntroductionCHAPTERTWO-AbouttheRaspberryPiCHAPTERTHREE-InstallanOperatingSystemCHAPTERFOUR-SetupPythonandtryouttheGPIOsCHAPTERFIVE-UseaDHT22sensorCHAPTERSIX-CreateawebapplicationstackontheRPiCHAPTERSEVEN-ServingstaticassetsCHAPTEREIGHT-FlasktemplatesandCSSstylingCHAPTERNINE-ViewsensorreadingsviaawebbrowserCHAPTERTEN-SetupasimpledatabaseanduseittologsensordataCHAPTERELEVEN-SelectrecordstoviewbytimeanddateCHAPTERTWELVE-AddVisualisationsCHAPTERTHIRTEEN-ImprovetheUICHAPTERFOURTEEN-AdjustingforactualtimezoneCHAPTERFIFTEEN-LinkthetwopagesCHAPTERSIXTEEN-UploadingtoPlotlyCHAPTERSEVENTEEN-Conclusion