sâmbătă, 14 ianuarie 2017

Primii pasi cu Raspberry Pi (2) - GPIO

Salutare tuturor!

     In acest articol vom pune bazele lucrului cu GPIO-ul micutului computer numit Raspberry Pi. Daca nu ati parcurs articolul anterior va recomand sa o faceti acesand pagina de aici

     GPIO este o abreviere de la englezescul general purpose in-out. Cu alte cuvinte, pe langa pinii microprocesorului (si nu ai microcontroller-ului :) ) care asigura functii speciale, exista si un set de pini care ne sunt pusi la dispozitie si pe care putem sa ii programam dupa bunul plac. 

In functie de versiunea de Raspberry Pi pe care o detineti acesti pini sunt diferiti. Prin diferiti se intelege atat pozitionarea lor pe placa cat si functiile auxiliare pe care le indeplinesc. Pentru acest articol am folosit un Raspberry Pi V2 al carui header este urmatorul:


Daca detineti o alta versiune va recomand cu caldura sa accesati pagina aceasta.

ATENTIE: 


TENSIUNEA PE CARE O FURNIZEAZA UN PIN ESTE DE 3.3V, ATUNCI CAND ESTE CONFIGURAT CA SI PIN DE IESIRE. LA FEL SI IN CAZUL IN CARE ESTE CONFIGURAT CA PIN DE INTRARE: VALOAREA MAXIMA PE CARE O POATE "CITI" ESTE TOT DE 3.3V. O TENSIUNE MAI MARE SI... PLICI! 
IN CAZUL CURENTILOR, LUCRURILE SUNT UN PIC MAI COMPLICATE: CURENTUL TOTAL PE CARE IL POATE FURNIZA - PENTRU TOTI PINII - ESTE DE 50mA. CONSIDERAND CA FOLOSIM TOTI CEI 17 PINI DISPONIBILI, CURENTUL MAXIM/PIN ESTE DE 50/17=3mA. IN GENERAL, CA REGULA DE BAZA CURENTUL PER PIN TREBUIE SA FIE CAT MAI MIC CU PUTINTA!

Cum in articolul de fata ne vom juca cu leduri, consider ca este necesar sa explic un pic montajul:







     Asadar bargraph-ul contine 10 leduri. Conform datasheet-ului, caderea de tensiune pe fiecare led este de 2V. Din totalul de 3.3V mai raman 1.3V, caderea de tensiune pe fiecare rezistenta.Cunoscand valoarea unei rezistente (1000 ohmi) se poate calcula curentul consumat de fiecare led: 1.3/1000=1.3mA. Cum avem 10 leduri, atunci cand toate sunt aprinse, curentul maxim consumat de pe bara de 3.3V este de 13mA. Deci este OK.
     Intregul montaj a fost conceput pe un breadboard. Si sa nu uit, va recomand din tot sufletul sa cumparati un adaptor pentru breadboard la header-ul Raspberry Pi.



Programarea...

     In scop demonstrativ au fost realizate 4 programe, de la simplu la complex. Ce a iesit puteti vedea in clipul de mai jos:



     Inainte de a ne baga nasul in Python trebuie instalata libraria care ne ofera acces la GPIO. Pentru aceasta realizati o conexiune la Pi printr-un client SSH (Putty, MobaXterm) si dupa logare tastati urmatoarea coamanda:



sudo apt-get install rpi.gpio

Dupa instalarea librariei urmati urmatorii pasi:

  1. Porniti programul MobaXterm;
  2. Deschideti o sesiune SSH cu Raspberry Pi (vezi articol anterior);
  3. Deschideti tabul  Sftp;
  4. Creati un folder nou: GPIO_SCRIPTS;



Acum in interiorul folder-ului creati primul fisier python cu numele : GPIO_0.PY:




Click dreapta pe fisierul noucreat si alegeti deschiderea acestuia cu editorul incorporat in MobaXterm:



Primul program...

Dupa cum spuneam si in randurile de mai sus cele 4  programe sunt realizate de la simplu la complex. Primul program va "clipi" doua leduri pe rand la interval de o secunda. Listingul il gasiti mai jos:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import RPi.GPIO as gp
import time
import os
os.system('clear')
print('Running...')
gp.setmode(gp.BCM)

pin1 =26
pin2=19

cnt=0;
gp.setup(pin1,gp.OUT)
gp.setup(pin2,gp.OUT) 

while(1):
  gp.output(pin1,1)
  gp.output(pin2,0)
  time.sleep(1)
  gp.output(pin1,0)
  gp.output(pin2,1)
  time.sleep(1)

Pentru cei care au experienta cu Arduino codul pare familiar: 

  • In linia 1 de cod se importa libraria GPIO;
  • Pentru a putea avea acces la delay-uri se importa modulul time;
  • In liniile 12 si 13  se seteaza directia pininilor, in cazul de fata este OUT
  • Incepand cu linia 15 se creaza bucla infinita care va "clipi" ledurile la interval de 1 secunda.
     Am lasat la urma explicatia pentru cea mai importanta linie de cod: linia cu numarul 6. Dupa cum spuneam si la inceputul articolului arnajarea pinilor pe placa este un pic confuza. Exista doua numerotari: numerotarea pinilor in ordinea plantarii acestora pe header (de la 1 la 40) si numerotarea in functie de terminalele microprocesorului, cea in care apare prefixul GPIO. Cand se importa modulul GPIO ambele optiuni pot fi luate in calcul.

Daca se doresete utilizarea pinilor in ordinea plantarii pe placa se va folosi instructiunea,

gp.setmode(gp.BOARD)

iar daca se doreste utilizarea pinilor in functie de numele pe care le au terminalele microprocesorului, se va utiliza instructiunea


gp.setmode(gp.BCM)

In cazul de fata, deoarece mi-a fost mult mai comod sa folosesc denumiriele deja gravate pe adaptorul de breadboard, am utilizat cea de-a doua varianta. Astfel, ca o ultima lamurire, in cazul de fata GPIO26 corespunde de fapt celui de-al 37-lea pin iar GPIO19 celui de-al 35-lea.

Mare atentie: alegeti intotdeauna varianta pentru care ati facut montajul deoarece exista posibilitatea (destul de mare) sa ardeti computerul.

Copiati codul de mai sus (fara numarul liniei de cod) si paste in editorul de text deschis. Salvati, dupa care in SSH tastati scuccesiv comenzile:


cd GPIO_SCRIPTS
sudo python3 GPIO_0.PY




Programul va rula. Nu uitati ca sunteti intr-o bucla infinita. Pentru a inchide programul tastati in acelasi timp CTRL+C.

Al doilea program...

Cel de-al doilea program este ceva mai complicat, dar nu foarte... Acesta va aprinde si va stinge ledurile succesiv, la interval de o secunda.
Urmand pasii de mai sus creati un al doilea fisier numit de data aceasta GPIO_1.PY. Si copiati listingul de mai jos:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import RPi.GPIO as gp
import time
gp.setmode(gp.BCM)

pins=[26,19,13,6,5,22,27,17,4,21]

cnt=0;
for i in pins:
 gp.setup(i,gp.OUT)
 gp.output(pins[cnt],False) 
 cnt=cnt+1
 
while(1):
 for i in range(0,10):
  gp.output(pins[i],True)
  time.sleep(1);
  
 for i in range(0,10):
  gp.output(pins[i],False)
  time.sleep(1); 

Rulati programul cu comanda:


sudo python3 GPIO_1.PY

Al treilea program...

     Daca pana acum "am fost martori" la ceea ce face Pi-ul, acum a sosit momentul sa ii trimitem comenzi. Programul de fata aprinde si stinge ledurile in functie de numarul acestora care a fost preluat de la tastatura. Nu voi explica prea mult programul  deoarece contine elemente proprii limbajului de programare Python3 lucru care nu face obiectul articolului de fata. Pentru un tutorial foarte bun in ceea ce priveste limbajul de programare mai sus mentionat va recomand cu caldura sa mergeti aici.

Urmati pasii anteriori si creati un fisier denumit GPIO_1.PY. Copiati si salvati listingul urmator:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import RPi.GPIO as gp
import time
import os
from colorama import Fore, Back, Style

os.system('clear')

gp.setmode(gp.BCM)

pins=[26,19,13,6,5,22,27,17,4,21]
state=[0,0,0,0,0,0,0,0,0,0]
check=False
cnt=0;
for i in pins:
 gp.setup(i,gp.OUT)
 gp.output(pins[cnt],False) 
 cnt=cnt+1
def printLeds():
 print(Fore.YELLOW+"RASPBERRY PI SSH LED CONTROL")
 print(Back.BLACK+"",end='\n')
 for i in state:
  if i==1:
   print(Back.RED+' ',end='')
  else:
   print(Back.WHITE+' ',end='')
  print(Fore.WHITE+Back.BLACK+"|",end='') 
 print(Back.BLACK+"",end='\n')
 for i in range(0,10):
  print(Fore.GREEN+str(i),end='')
  print(Fore.WHITE+Back.BLACK+"|",end='')
 print(Back.BLACK+"",end='\n')

printLeds() 
while(1):
 print("")
 pin=input(Fore.GREEN+"Insert led number:")
 if pin.isnumeric():
  check=False
  k=int(pin)
  #check if it's a valid led number
  for i in range(0,10):
   if k==i:
    os.system('clear')
    print("")
    check=True;
    if state[i]==0:
     state[i]=1
     gp.output(pins[i],True)
    else:
     state[i]=0
     gp.output(pins[i],False)
    printLeds() 
  if check==False:
   print(Fore.RED+"No led number!")
 else:
  print(Fore.RED+"Insert a number in range 0-9")


Rulati programul cu urmatoarea comanda:




sudo python3 GPIO_2.PY



Dupa incarcare puteti comanda ledurile tastand numarul ledului (de la 0 la 9) iar ledul isi va schimba starea.




Al patrulea program...


     Programul de mai sus ne permite in sfarsit sa interactionam cu Raspberry Pi. Problema este ca nu putem citi starea curenta a ledurilor. Acest lucru este de dorit daca sunt mai multi utilizatori care vor sa comande ledurile. Urmatorul program rezolva (partial) problema. Starea ledurilor este salvata intr-un fisier. Acest lucru ne permite sa rulam mai multe instante ale programului si chiar daca sunt procese diferite toate vor imparti aceeasi resursa: fisierul text in care sunt salvate starile. Cum python nu ne permite (in mod direct) sa aflam daca un fisier este deschis sau nu, mecanismul prin care se incerca evitarea scrierii concomitente de catre doi sau mai multi utilizatori in fiser este... redenumirea fisierului. Ca si in cazul programului anterior va recomand sa urmati cat mai multe tutoriale Python3.

Fisierul se va numi "status", si va trebui sa se afle in acelasi folder ca si programul. Continutul initial al fisierului este urmatorul:


0,0,0,0,0,0,0,0,0,0

Dupa cum se observa, initial toate ledurile sunt stinse.

Cunoscand deja mecanismul vom crea al 4-lea (si ultimul fisier) cu denumirea GPIO_3.PY. Apoi vom copia codul din listingul de mai jos:



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
import RPi.GPIO as gp
import time
import os
from colorama import Fore, Back, Style

os.system('clear')

gp.setmode(gp.BCM)

pins=[26,19,13,6,5,22,27,17,4,21]
state=[0,0,0,0,0,0,0,0,0,0]
check=False
cnt=0;
for i in pins:
 gp.setup(i,gp.OUT)
 gp.output(pins[cnt],False) 
 cnt=cnt+1
def printLeds():
 print(Back.BLACK+"",end='\n')
 for i in state:
  if i==1:
   print(Back.RED+' ',end='')
  else:
   print(Back.WHITE+' ',end='')
  print(Fore.WHITE+Back.BLACK+"|",end='') 
 print(Back.BLACK+"",end='\n')
 for i in range(0,10):
  print(Fore.GREEN+str(i),end='')
  print(Fore.WHITE+Back.BLACK+"|",end='')
 print(Back.BLACK+"",end='\n')

  
def renameFile():
 r=False
 while(r==False):
  try:
   os.rename('status','temp')
   r=True
  except IOError:
   r=False



def writeFile():
 renameFile()
 print(Fore.YELLOW+"RASPBERRY PI SSH LED CONTROL")
 print('Writing data...')
 try:
  temp=open('temp','r+')
  a=''
  for i in state:
   a=a+str(i)+',' 
  temp.write(a[0:len(a)-1])
  temp.close()
  os.rename('temp','status')
  print('Writing complete!')
 except IOError:
  print('File not found...')


def readFile():
 os.system('clear')
 renameFile()
 print(Fore.YELLOW+"RASPBERRY PI SSH LED CONTROL")
 print('Reading data...')
 try:
  temp=open('temp','r+')
  a=temp.readline().split(',')
  for i in range(0,10):
   state[i]=int(a[i])
   gp.output(pins[i],state[i]) 
  temp.close()
  os.rename('temp','status')
  print('Reading complete!') 
  printLeds()
 except IOError:
  print('File not found...')
  
readFile()

while(1):
 print("")
 pin=input(Fore.GREEN+"Insert led number or r to read status:")
 if pin.isnumeric():
  check=False
  k=int(pin)
  #check if it's a valid led number
  for i in range(0,10):
   if k==i:
    os.system('clear')
    print("")
    check=True;
    if state[i]==0:
     state[i]=1
     gp.output(pins[i],state[i])
    else:
     state[i]=0
     gp.output(pins[i],state[i])
    writeFile()  
    printLeds()
    break 
  if check==False:
   readFile()
   print(Fore.RED+"No such command...")
 elif pin=='r':
  readFile()
 else:
  readFile()
  print(Fore.RED+"No such command...")



Cam atat pentru azi. Fisierele cu codurile sursa se pot descarca de aici.

Sa auzim de bine !!!!

P.S. Nu va sfiiti sa lasati comentarii !!!

Niciun comentariu:

Trimiteți un comentariu