Rakentelin tuossa kesän alussa kotelon, johon sijoitin Raspberry Pi piirin, PiFace io-yksikön, akun, muuntajan, raspi kameran, kytkimiä nappeja ledejä ja pari mittaria. Tässä artikkelissa kerron millainen kokoonpano tuli ja mitä sillä teen.
Yleistä
Pääajatuksena oli saada raspi toimaan akun varassa, jotta voin tehdä timelapse kuvaotoksia luonnossa. Kameran kohdistusta varten hankin pienen näytön, joka myös saa sähkönsä akusta. Myös ulkoisen IO:n käyttö kiinnosti ja päädyin ostamaan PiFace io-yksikön. Itsekin varmasti olisin saanut tehtyä jonkinlaisen io-liitännän, mutta tuo valmis piiri oli nopein ja turvallisin tapa.
Laitteisto
Raspberry Pi B versio toimii siis järjestelmän aivoina. Ohjelmat teen pääosin python3 kielellä ja kameran ohjaukseen olen netistä kopioinut komentoja.
Kamera
Kamerana toimii raspin kamera moduuli jota ohjaan raspistill komennoilla. http://www.raspberrypi.org/documentation/usage/camera/raspicam/raspistill.md http://www.raspberrypi.org/product/camera-module/
PiFace Digital
Tässä raspin lisämodulissa on 8 tuloa ja 8 lähtöä. Kaikkien kahdeksan lähdön rinnalla on jo valmiiksi ledit ja kahden ensimmäisen lähdön rinnalla on myös pienet releet (20V max). Itseä ainakin hämäsi mainospuheet ja käsitin että olisi ollut 8 lähtöä + 2 relelähtöä mutta eipä ollut. Piirissä on myös 4 nappia, jotka ovat tietysti ensimmäisten neljän tulon rinnalla. Piiri on siinämielessä hyvä, että se on turvallinen tapa keskustella ohjelmien kanssa, eikä tarvitse pelätä raspin hajoamista jos tekee kytkentävirheen. http://www.piface.org.uk/
LCD näyttö
Näytön ostin Jammaa.com sivuilta ja se on mallia EH7000. Tarkkuus on 800x600, mutta kuva on aika epäselvä. Mutta alle 90€ hintaan Suomesta olen tyytyväinen hinta/laatu suhteeseen. Näyttö on tarkoitettu autoon dvd soittimelle ja siinä on kaksi video tuloa ja jopa kaukosäädin. En ole vielä selvittänyt kuinka saan konsolin fontin muutettua sopivaksi näytölle, mutta pystyn kirjoittamaan ohjelmaa pelkän näytön avulla kun käytän hieman mielikuvitusta kirjaimien kanssa Hyvät puolet se että toimii 12v kuten oli vaatimus, näyttöön tulee vain yksi moninapaliitin eli saan sen irti helposti ja muutenkin näyttö on hyvän kokoinen.
Kuvassa teksti on vielä huonomman näköinen kuin livenä. Parempiakin vaihtoehtoja on kyllä tarjolla, jopa suoraan raspia varten suunniteltuja, joten voisin ehkä suositella sellaista mielummin kuin tätä.
IR valonheitin
Tilasin ebayn kautta IR valonheittimen ja asensin sille liitoksen koteloon. IR heitin tarvitsee siis IR kameran eli Raspin NOIR kameran. IR heitin käyttää n.600mA virtaa päälläollessaan, joten tulen asentamaan toisen relelähdön katkomaan IR heittimen sähköä, jolloin valo on päällä vain tarpeen mukaan.
Kotelo
Kotelo on fiboxin pieni muovikotelo, joka sattui sopivasti olemaan tyhjänä nurkissa. Kanteen asensin voltti ja ampeerimittarin. Niiden avulla pystyn tarkkailemaan akun jännitettä ja virrankulutusta ja sen mukaan arvioimaan varauksen määrää. Akkuna on vanhan ups yksikön akku, 17Ah. Ensimmäisessä timelapse viritelmässä laitoin kameran kuvaamaan 24h ajanjaksoa ja akku oli hyytynyt jo 18h jälkeen, vaikka virrankulutus oli ollut vajaa 0.3-0.4A!? Eli uusi akku on ostoslistalla. Tai sitten olen käsittäyt jotakin väärin…
Kytkentä
Akusta plussa menee ledillä varustettuun kytkimeen, siitä mittareille ja niiltä taas kolmelle ledillä varustetulle kytkimelle. Kytkimillä saan katkottua päävirtaa, 5V muuntajan sähköä, lcd:n syöttöä ja IR lampun lähdön sähköä.
Muuntajana toimii autoihin tarkoitettu monijännitemuuntaja joka muuttaa 12V useiksi eri jännitteiksi. Itsellä on käytössä aina 5V, joka on kytketty USB “hubiin”. Hubista lähtee micro usb liitin raspille ja toinen on varalla ulkoista langatonta verkkolaitetta varten. Myöhemmin otan hubista myös servolle käyttösähkön, jolloin pääsen kääntämään kameraa tarpeen mukaan.
Kanteen asensin myös 4 kytkintä, 4 nappia ja 8 lediä jotka on johdotettu PiFace Digital korttiin. Niiden avulla pystyn ohjaamaan raspia ilman näppäimistöä. Jälkiviisaana voisi todeta, että olisinpa tehnyt kaikista vesitiiviit.
Kotelon kylkeen asensin kamera telineen todella hienolla viritelmällä. Telineeseen voin vaihtaa tarpeen mukaan ir kameran.
Kotelo on aika hurjan näköinen ja puolustuksekseni voin sanoa vain että tein sen todella nopealla aikataululla, jotta sain laitteen mukaani lomareissulleni. Reissulla olen ottanut muutamia timelapsevideoita ja siinä se on palvellut hyvin, mutta silti harmittaa huolimaton työnjälki. Ehkä syksyllä ostan alumiinisen “salkkumallin” kotelon ja rakennan sinne kunnon telineet.
Käyttö
Olen käyttänyt laitetta timelapse videoiden kuvien tallentamiseen. Kameran ohjaukseen käytettävä raspistill komento on monipuolinen ja todella näppärä, joten olen käyttänyt sitä. Tein pienen python skriptin, joka lähtee käynnistyksen yhteydessä pyörimään taustalle ja kun painan sinistä nappia, kamera näyttää muutaman sekunin ajan kuvan, jolloin voin tarkistaa kohdistuksen. Kun kohdistus on hyvä, käännän kytkimen ja skripti käynnistää raspistill komennon, joka nappaa kuvia halutulla aikavälillä. Kun kuvia on otettu tarpeeksi, saa raspin sammutettua toisesta kytkimestä. Tein myös koodiin automaattisen sammutuksen, jotta laite sammuu ennenkuin akku loppuu täysin.
Nuo kytkimet ja napit ovat hyvänä apuna kun yritän opetella tuota python kieltä. Eräänä päivänä sain päähäni tehdä speden speleistä tutun reaktio pelin. Ledit syttyvät random järjestyksessä ja nappeja pitää painaa vastaavassa järjestyksessä. Pelin edetessä ledien syttymisen tiheys kasvaa ja pelin haaste kasvaa. Ohessa koodi myös tästä pelistä.
Teen luultavasti vielä liitokset laturille, että voin pitää aina laturia kiinni järjestelmässä ja akku turvaa vain sähkökatkosten varalla. Tällöin saan muutettua systeemin valvontakamera käyttöön. Olen jo tehnyt aiemmin sellaisen järjestelmän, että ps3:n kameraa hyväksikäyttäen Motion kirjastolla olen kaapannut kuvia ja ladannut niitä dropboxiin kun olen ollut reissussa. Etähallintaan kannattaa myös tutustua NeoRouter ohjelmaan. Sen avulla sain tehtyä etäyhteyden vaikka kännykästä raspiin.
Reaktiopelin koodi spede.py:
import pifacedigitalio
import time
import datetime
import os
import random
GameOver = 0
GameOn = 0
EdAika = 0
lista = []
laskuri = 0
edAjat = [0,0,0,0]
TestiSallinnat = [0,0,0,0]
edNum = 0
def aika():
m = int( round( time.time() * 1000 ) )
return m
def sininen_nousu(event):
global TestiSallinnat
global edAjat
#sininen nappi painettu
#print("Painoit juuri sinistä")
edAjat[0] = aika()
if TestiSallinnat[0] ==1 :
TestiSallinnat[0] = 0
testi(4)
def punainen_nousu(event):
global TestiSallinnat
global edAjat
#punainen nappi painettu
#print("Painoit juuri punaista")
edAjat[1] = aika()
if TestiSallinnat[1] ==1 :
TestiSallinnat[1] = 0
testi(5)
def vihrea_nousu(event):
global TestiSallinnat
global edAjat
#vihreätä nappia painettu
#print("Painoit juuri vihreää")
edAjat[2] = aika()
if TestiSallinnat[2] ==1 :
TestiSallinnat[2] = 0
testi(6)
def keltainen_nousu(event):
global TestiSallinnat
global edAjat
#keltaista nappia painettu
#print("Painoit juuri keltaista")
edAjat[3] = aika()
if TestiSallinnat[3] ==1 :
TestiSallinnat[3] = 0
testi(7)
def testi(numero):
global laskuri
global GameOver
global lista
if GameOver == 0:
if len(lista) > 0:
i = lista.pop()
if numero != i :
print("Game Over")
print("Tulos: " + str(laskuri) )
ennatys = tarkista_ennatys(laskuri)
if ennatys:
print("Uusi ennatys!")
laskuri = 0
GameOver = 1
valayta()
else:
laskuri = laskuri + 1
GameOver = 0
else:
print("Game Over!")
print("Tulos: "+ str(laskuri))
ennatys = tarkista_ennatys(laskuri)
if ennatys:
print("Uusi ennatys!")
laskuri = 0
GameOver = 1
valayta()
#määritetään io objekti
pfd= pifacedigitalio.PiFaceDigital()
#Interrrup asetukset
listener = pifacedigitalio.InputEventListener(chip = pfd)
listener.register(4, pifacedigitalio.IODIR_RISING_EDGE, sininen_nousu)
listener.register(5, pifacedigitalio.IODIR_RISING_EDGE, punainen_nousu)
listener.register(6, pifacedigitalio.IODIR_RISING_EDGE, vihrea_nousu)
listener.register(7, pifacedigitalio.IODIR_RISING_EDGE, keltainen_nousu)
listener.activate()
# pfd.output_pins[i].toggle()
# pfd.output_pins[1].value = pfd.input_pins[1].value
# pfd.input_pins[7].value
# pfd.output_port.all_on()
# pfd.output_port.all_off()
def sallinnat(pin):
#Tarkistetaan nappien tilat ja asetetaan interruppien sallinnat
global TestiSallinnat
global edAjat
if pfd.input_pins[pin + 4].value == 0:
t = aika() - edAjat[pin]
if t > 200:
TestiSallinnat[pin] = 1
else:
edAjat[pin] = aika()
def tauko_aika():
global laskuri
t = laskuri * 10
s = 1000 - t
if s < 200:
return 200
else:
return s
def valayta():
pfd.output_port.all_on()
time.sleep(0.2)
pfd.output_port.all_off()
time.sleep(0.2)
pfd.output_port.all_on()
time.sleep(0.2)
pfd.output_port.all_off()
def tarkista_ennatys(tulos):
#Tarkistetaan onko tulos parempi kuin ennatys. Jos on kirjataan uusi tulos
#Tarkistetaan loytyyko tiedostoa
polku = os.getcwd() + "/spede_ennatys.txt"
if os.path.isfile(polku):
fh = open(polku, "r")
luku = fh.read()
fh.close()
if int(luku) < tulos:
fh = open(polku, "w")
fh.write(str(tulos))
fh.close()
return 1
else:
return 0
else: #ei tiedostoa, luodaan uusi ennatystiedosto
fh = open(polku, "w")
fh.write(str(tulos))
fh.close()
return 1
def uusi_numero():
global edNum
r = random.randint(4,7)
if r == edNum:
r = r - 1
if r == 3:
r = 7
edNum = r
return r
def main():
global GameOver
global GameOn
global lista
global EdAika
pois = 0
#Sininen kytkin
if pfd.input_pins[0].value:#Poistumisehto
#Poistutaan ohjelmasta.
pois = 1
#punainen kytkin
if pfd.input_pins[1].value:
dum = 1
#vihreä kytkin
if pfd.input_pins[2].value:
dum = 3
#keltainen kytkin
if pfd.input_pins[3].value:
if GameOn == 0:
if GameOver == 0:
GameOn = 1
print("Game On!")
pfd.output_port.all_on()
time.sleep(1)
pfd.output_port.all_off()
EdAika = aika()
else:
GameOver = 0
GameOn = 0
lista = [] #Tyhjennetään lista
#Pääkierto
if GameOver == 0:
if GameOn == 1:
AikaNyt = aika()
Tauko = tauko_aika()
if AikaNyt - EdAika >= Tauko: #Tauko värien vaihdon välillä
EdAika = AikaNyt
#Välitaan väri
valinta = uusi_numero()
#Lisätään väri listaan ja sytytetään valo
lista.insert(0,valinta)
pfd.output_pins[valinta].turn_on()
#Pidetään valoa päällä hetki ennen sammutusta
paalla_aika = Tauko / 2
while aika() - EdAika < paalla_aika:
sallinnat(0)
sallinnat(1)
sallinnat(2)
sallinnat(3)
if pfd.output_port.value == 0:
break
time.sleep(0.02)
pfd.output_port.all_off()
Ed_Aika = aika()#aika talteen
sallinnat(0)
sallinnat(1)
sallinnat(2)
sallinnat(3)
time.sleep(0.02)#while loopin tauotus jotta ei kuirmiteta cpu:ta
return pois
print("---------------------------- Speden nopeuspeli ----------------------------")
print("Keltaisesta kytkimesta peli kayntiin. Ohjelman lopetus sinisella kytkimella")
while True:
k=main()
if k:
break
#Poistuttu while loopista, sammutetaan lähtöt ja irroitetaan kortti käytöstä
listener.deactivate() #Otetaan interrupit pois käytöstä
pfd.output_port.all_off()
pfd.deinit_board()
Kameran ohjauskoodi kameran_ohjaus.py:
import pifacedigitalio
import time
import datetime
import os
SininenNousu_EdAika=0
Timelapse_Paalla = 0
Kaynnistysaika = 0
def aika():
m = int( round( time.time() * 1000 ) )
return m
def sininen_nousu(event):
#sininen nappi painettu
global SininenNousu_EdAika
global Timelapse_Paalla
if Timelapse_Paalla == 0:
erotus = aika() - SininenNousu_EdAika
#jos edellisesta interrupista kulunut tarpeeksi kauan, suoritetaan tama
if erotus > 5000:
pfd.output_pins[4].turn_on()
SininenNousu_EdAika = aika()
os.system("raspistill -f &")
time.sleep(1) #Väläytetään lediä että tiedetään toimivan
pfd.output_pins[4].turn_off()
def punainen_nousu(event):
#punainen nappi painettu
#print("Painoit juuri punaista")
dum = 2
def vihrea_nousu(event):
#vihreätä nappia painettu
#print("Painoit juuri vihreää")
dum = 3
def keltainen_nousu(event):
#keltaista nappia painettu
#print("Painoit juuri keltaista")
dum = 4
print(str(paallaolo_aika()))
#Maaritetaan IO objekti
pfd= pifacedigitalio.PiFaceDigital()
#Interrrup asetukset
listener = pifacedigitalio.InputEventListener(chip = pfd)
listener.register(4, pifacedigitalio.IODIR_RISING_EDGE, sininen_nousu)
listener.register(5, pifacedigitalio.IODIR_RISING_EDGE, punainen_nousu)
listener.register(6, pifacedigitalio.IODIR_RISING_EDGE, vihrea_nousu)
listener.register(7, pifacedigitalio.IODIR_RISING_EDGE, keltainen_nousu)
listener.activate()
#komentoja
# pfd.output_pins[i].toggle()
# pfd.output_pins[1].value = pfd.input_pins[1].value
# pfd.input_pins[7].value
# pfd.output_port.all_on()
# pfd.output_port.all_off()
# pfd.output_pins[0].turn_on()
# pfd.output_pins[0].turn_off()
def paallaolo_aika():
global Kaynnistysaika
return int(round(time.time())) - Kaynnistysaika
def main():
global Timelapse_Paalla
pois = 0
#Sininen kytkin
if pfd.input_pins[0].value:#Poistumisehto
#Poistutaan ohjelmasta.
pois = 1
#punainen kytkin
if pfd.input_pins[1].value:
#kaynnistetaan timelapseohjelma
if Timelapse_Paalla == 0:
Timelapse_Paalla = 1
os.system("sudo /etc/init.d/runtimelapse start")
else:
if Timelapse_Paalla:
os.system("sudo /etc/init.d/runtimelapse stop")
Timelapse_Paalla = 0
#vihreä kytkin
if pfd.input_pins[2].value:
dum = 3
#keltainen kytkin
if pfd.input_pins[3].value:
dum = 4
#Jos koodi pyörinyt tarpeeksi kauan aikaa, poistutaan ohjelmasta
akkukesto = 18 # tunteina
akkukesto = akkukesto * 60 * 60
if paallaolo_aika() > akkukesto:
pois = 1
if Timelapse_Paalla:
os.system("sudo etc/init.d/runtimelapse stop")
return pois
def vieritys():
#rullataan ledit lapi jarjestyksessa
tauko = 0.1
pfd.output_pins[0].turn_on()
i=0
while i<7:
time.sleep(tauko)
pfd.output_pins[i].turn_off()
pfd.output_pins[i+1].turn_on()
i=i+1
time.sleep(tauko)
pfd.output_port.all_off()
#Kun ohjelma kaynnistyy, valaytetaan ledeja, jotta tiedetaan etta kay
vieritys()
Kaynnistysaika = int(round(time.time()))
#Itse paa looppi jossa kutsutaan main ohjelma
while True:
k=main()
if k:
break
else:
time.sleep(0.5)
#Poistuttu while loopista, sammutetaan lähtöt ja irroitetaan kortti käytöstä
listener.deactivate() #Otetaan interrupit pois käytöstä
pfd.output_port.all_on()
time.sleep(2)
pfd.output_port.all_off()
if pfd.input_pins[2].value == 0: #Sammutetaan vain jos vihreä kytkin ei ole päällä
os.system("sudo shutdown -h 1 &")
pfd.deinit_board()
#Sammutetaan järjestelmä 1min kuluttua
Ohjauskoodiin liittyvä raspistill komento capturetimelapse.sh:
filename=$(date -u +"%Y%m%d_%H%M%S")_%05d.jpg
/opt/vc/bin/raspistill -o /home/pi/images/$filename -w 1920 -h 1080 -tl 2500 -t 61200000 > /home/pi/camera.log 2>&1 &
#sudo update-rc.d -f runtimelapse remove ei käytössä
#sudo shutdown -h 5 ei käytössä