Introduktion till programmering

Va? Onsdag idag också!? Ja, det ser inte bättre ut 😉

Förra veckan tittade vi på Enkla program och funktioner. Jag har i flera veckor nu lovat att ta upp import av bibliotek. Och nu är det dags!

Vi har gått igenom några enklare program, och till och med programmerat en egen funktion, men vad gör man om den funktionalitet man behöver inte finns inbyggd i språket?

Man kan såklart skriva koden själv, men det kan ofta ta en hel del tid och antagligen finns det någon som redan löst samma problem, så varför inte använda sig av deras färdiga kod istället?

För att illustrera det här lite smart tänkte jag använda mig av den kod vi skrev i förra avsnittet, “intput”. Låt säga att vi skrivit den koden och vill använda oss av den i ett nytt program.

Om vi försöker anropa intput utan att importera den först, kommer vi att få följande felmeddelande:

>>> intput()

Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    intput()
NameError: name 'intput' is not defined
>>>

Det står att intput, mycket riktigt, inte är definierad. Vi har definierat den tidigare, men varje gång man startar om Python nollställs alla användardefinitioner. Så hur gör vi?

Enkelt! Vi sparar funktionen i en fil, “intput.py”, och importerar den i vårt nya program:

Intput.py (1616 nedladdningar )

 

Antingen kan vi placera filen i Pythons Lib-mapp (i mitt fall: “C:\Program Files\Python\Lib”), och kan då skriva:

from intput import *

Vill vi hellre ha samverkande filer som ligger i samma mapp skriver vi på samma sätt, men i mappen placerar vi också en tom fil som heter “__init__.py”. Detta gör vi för att signalera till Python att det är okej att importera från den här mappen. Vill vi ha en undermapp med våra källkodsfiler blir det också lite annat. Om vi t.ex. har filen “program.py” och sen en undermapp, “libs” i vilket vi har vår intput.py så får vi lägga till en __init__.py på samma sätt som ovan, men i koden skriver vi:

from libs.intput import *

“*” betyder att vi vill importera allt från biblioteket i fråga, så hade vi skrivit in fler funktioner hade dessa också laddats in.

Nu kan vi försöka köra vårt program, och se om det går bättre:

>>> from intput import *
>>> intput("nr 1: ")
nr 1: 12
12

Nu gick det betydligt bättre! Notera att den sista raden, “12”, är värdet som returneras av funktionen och eftersom vi inte anger vad som ska göras med det skrivs det ut.

Importera GPIO-biblioteket och göra enkla operationer

Efter många långa veckors väntan är vi äntligen redo att ge oss på GPIO på Raspberry Pi!

Biblioteket som behövs för att arbeta mot GPIO-portarna, RPi.GPIO, finns förinstallerat på nyare versioner av Raspbian. Med andra ord är det bara att börja koda!

Innan man börjar är det dock bra att ha lite referensmaterial, så man vet vilka GPIO-pinnar som gör vad. Raspi.tv har en bra sådan här.

För att förenkla det här kan man säga att varje pinne antingen skickar eller tar emot data. De röd- och svartmarkerade gör alltid samma sak (3.3V, GND (jord) och 5V). GPIOxx-portarna går att bestämma vad de ska göra. Antingen läser de av status (t.ex. om man vill känna av i vilket läge en brytare är), eller så ändrar de status (t.ex. om man vill styra något, låt säga en elmotor, med Pi:n). Varje pinne är antingen 1 eller 0 i datorspråk, men i den analoga världen motsvaras detta av 3.3V för etta och jord för nolla.

Detta betyder alltså att om man sätter t.ex. GPIO2 till avläsningsläge, och ansluter en av 3.3V-portarna till denna, kommer GPIO2 vid avläsning alltid visa “1”. Kopplar man den till GND visar den alltid “0”. 1/o kan också representeras som GPIO.HIGH respektive GPIO.LOW, eller om man föredrar det, ett boolean-värde (Sant eller falskt).

Som man kan läsa i referensmaterialet måste man först och främst bestämma hur portarna numreras i koden med något av följande kommandon:

# choose BOARD or BCM  
GPIO.setmode(GPIO.BCM)               # BCM for GPIO numbering  
GPIO.setmode(GPIO.BOARD)             # BOARD for P1 pin numbering

Att använda GPIO-numrering (GPIO.BCM) är väldigt rörigt eftersom GPIO2 exempelvis är på pin 3, GPIO17 är på pin 11 och så vidare. Det tycker åtminstone jag gör det hela väldigt krångligt, så jag rekommenderar GPIO.BOARD istället så blir det lite lättare utan att sitta med referensmaterialet hela tiden.

Därefter behöver man bestämma, för varje pin man vill använda, om den ska skicka eller ta emot data. Det görs så här:

pin1 = 3
pin2 = 5
#osv

GPIO.setup(pin1, GPIO.IN)
GPIO.setup(pin2, GPIO.OUT)

GPIO.setup(pin3, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(pin4, GPIO.OUT, initial=1)

På första raden sätter vi “pin1” (som ska vara ett heltal, och ett heltal som motsvarar en GPIO-port vilket går att avläsa i referensmaterialet) till att ta emot data. Eftersom den bara läser av status momentant och det är antingen 1 eller 0 kan man, tack vare att Python likställer 1/0 med True/False, använda avläsningen i både loopar och if-satser för att avgöra om en viss kod ska köras. Fräsigt va!?

Andra raden sätter “pin2” till GPIO.OUT, vilket innebär att den ska användas för att skicka data. Även outputs går att läsa av status på på samma sätt som inputs, för det är inte alltid helt självklart att vi vet vad den har för status även om den sätts i koden.

Tredje raden, där vi sätter “pin3” till IN, är lite annorlunda. Här lägger vi till en “Pull up”, vilket betyder att inporten som standard kommer vara 1. Man kan också skriva “GPIO.PUD_DOWN” för att omvandla porten till en “Pull down” som defaultar till 0. Detta är bra om man vill minimera risken för “flimmer”, vilket annars kan göra att det blir svårt för Pi:n att tolka huruvida det är 1 eller 0.

När vi nu har pin1 och pin3 som inputs och pin 2 och pin 4 som outputs, kan vi hämta indata respektive sätta utdata på dessa så här:

GPIO.output(pin2, 1)
GPIO.output(pin4, 0)

status = GPIO.input(pin1)

if GPIO.input(pin3):
	print("sant")


GPIO.cleanup()

Första raden sätter helt enkelt pin2 till 1, och andra sätter pin4 till 0.

I nästa steg skapar vi en variabel, “status”, som vi sätter till det värde som kommer in på pin1 (antingen 0 eller 1, såklart).

Därefter använder vi en if-sats, som skriver ut strängen “sant” om det råkar vara så att det värde som kommer in på pin3 är 1/True.

Det sista raden gör en så kallad “cleanup”, och städar bort allt GPIO-relaterat ur datorns minne. Detta för att minnet inte ska bli fullt med saker som inte längre används. Detta anrop gör man såklart först när man är färdig med all kod som jobbar mot GPIO-porten, så vanligt är att man t.ex. anropar den när programmet avslutas. Mer om minneshantering kan vi ta upp i något senare inlägg om det är efterfrågat 🙂

Det var allt för denna gång! Nästa gång kan vi titta på lite mer avancerade datatyper och hur man arbetar över matriser! Eller kanske något helt annat, vem vet! Ses då! 🙂

Uppdatering: Nästa del i serien finns nu upplagd!