Raspberry Pi - kotelointi muutaman lisälaitteen kanssa

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.
jarjestelma.jpg

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/
kamera.jpg

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/
piface.jpg

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 :smiley: 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.
naytto.jpg
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
loota.jpg
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.
esikatselu.jpg
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ä