[WikiItic] [TitleIndex] [WordIndex

1. Veu IP amb Asterisk

Integrants del grup : Àlex Catllà, Jose Manuel Randos, Edgar Costa i Ferran Llamas

Data : 11/06/2013

Taula de continguts:

1.1. Introducció

Aquest treball consisteix en oferir un servei de telefonia sobre IP, el que s'acostuma a anomenar servei de veu IP. Per oferir aquest servei ens hem centrat en la plataforma de telefonia Asterisk, que és un projecte open source dissenyat per funcionar sobre Linux. La gran flexibilitat que proporciona degut a la seva natura i la bona documentació existent fan d'Asterisk la millor opció per a montar un sistema de veu sobre IP.

La documentació que hem utilitzat en el treball és principalment : The definitive guide, Asterisk. Però aquesta wiki també està molt bé.

Els principals objectius s'han definit en aquest treball són, de menys a més complexitat:

La majoria dels objectius que ens vam marcar han estat assolits. Una excepció ha estat la de la passarel·la als serveis de Google Talk a causa de la mala documentació existent i que el mòdul que implementa la passarel·la encara està en desenvolupament.

1.2. Conceptes Bàsics

Abans de començar, cal estar al cas de la terminologia que s'útlilitza en veuIP, en general i en Asterisk més concretament.

1.2.1. Canal

Connexió per la qual viatgen les trucades entrants o sortints del sistema Asterisk . En el nostre cas configurem el canal per el protocol de veuIP SIP i canal del protocol de missatgeria XMPP.

La centraleta Asterisk permet comunicar diferents canals, per exemple una trucada entrant per canal SIP es pot comunicar amb un canal IAX de sortida.

1.2.2. Dialplan

És el fitxer que conté l'estructura bàsica del servidor telefònic. Es tracta de definir l'itinerari que recorren les trucades entrants i les trucades sortints tenint en compte el dispositiu que realitza la trucada i el dispositiu amb el qual es vol comunicar. La configuració del Dialplan es troba bàsicament en el fitxer extensions.conf, es tracta del lloc on es defineixen les extensions corresponents a cada context. El fitxer extensions presenta una estructura bàsica similar a la següent:

[nom_context]
exten => {extensio},{num_prioritat},{aplicacio1}
  same => n,{aplicacio2}
  ...
  same => n,{aplicaciom}

El mateix context pot contenir tantes extensions com siguin necessàries sempre que no es repeteixin. El dial plan pot contenir tants contextos com siguin necessaris.

1.2.3. Marcació

És el fet de polsar un conjunt de lletres o números. Per exemple la marcació 1-2-3-a-b-c, dona lloc a l'extensió 123abc.

1.2.4. Extensions

En terminologia Asterisk una extensió correspon a un nom en que pot contenir números i/o lletres, corresponents a una marcació, en el qual aquest nom se li assigna a un conjunt d'accions que es porten a terme, aquestes accions depenen del context al qual pertany el dispositiu que fa la marcació de l'extensió.

A les extensions s'accedeix quan:

1.2.5. Context

És l'eina per englobar en "grups" els diferents dispositius i a l'hora poder dur a terme diferents accions per la mateixa extensió de diferents contextos.

Els contextos es composen de dues parts:

[nom_context]
opcio_conf1 = valor1
opcio_conf2 = valor2
...
opcio_confn = valorn

En el cas de que un dispositiu no tingui assignat un context, totes les seves marcacions es dirigeixen directament a un context genèric anomenat [default].

1.2.6. Aplicacions

Les aplicacions es poden entendre com la "API" que proporciona Asterisk per dur a terme diferents tipus de tasques. Per exemple les més bàsiques són:

1.3. Trucades en xarxa d'àrea local (LAN)

Aquesta ha estat la primera prova a fer un cop instal·lada la centraleta. La intenció és poder comunicar dos dispositius a través dels respectius clients de VeuIP passant per la centraleta Asterisk. La configuració que cal fer per aquest exemple està explicada amb detall al capítol 5 de la referència principal, no obstant això, en farem cinc cèntims:

Partim de l'escenari que tant la centraleta Asterisk com els terminals que volem comunicar es troben connectats a la mateixa xarxa local. Principalment, cal fer tres tipus de configuracions:

  1. Informar a la centraleta dels terminals (canals) que volem comunicar, definir els seus paràmetres (contrasenya, nom d'usuari, etc) i registrar-los.
  2. Configurar els clients de veu IP per tal que puguin comunicar-se amb la centraleta (Sign In).
  3. Definir en el dialplan les extensions necessàries per poder encaminar les trucades entre els dos canals.

1.3.1. Configurar els canals

Per fer el pas 1, bàsicament cal modificar el fitxer sip.conf (iax.conf en cas d'utilitzar el protocol iax), que es troba a /etc/asterisk/sip.conf de la següent manera, seguint l'exemple del llibre:

[general]
context=unauthenticated         ; default context for incoming calls
allowguest=no                   ; disable unauthenticated calls
srvlookup=yes                   ; enabled DNS SRV record lookup on outbound calls
udpbindaddr=0.0.0.0             ; listen for UDP requests on all interfaces
tcpenable=no                    ; disable TCP support

[office-phone](!)   ; create a template for our devices
type=friend         ; the channel driver will match on username first, IP second
context=LocalSets   ; this is where calls from the device will enter the dialplan
host=dynamic        ; the device will register with asterisk
nat=yes             ; assume device is behind NAT
                    ; *** NAT stands for Network Address Translation, which allows  
                    ; multiple internal devices to share an external IP address.
secret=2222         ; a secure password for this device -- DON'T USE THIS PASSWORD!
dtmfmode=auto       ; accept touch-tones from the devices, negotiated automatically
disallow=all        ; reset which voice codecs this device will accept or offer
allow=ulaw          ; which audio codecs to accept from, and request to, the device
allow=alaw          ; in the order we prefer

; define a device name and use the office-phone template
[ferran](office-phone)
secret=1111

; define another device name using the same template
[edgar](office-phone)

Bàsicament aquí el que fem és definir una configuració [general] per a canals no autentificats o desconeguts i, més avall, definim un template de configuració que anomenem [office-phone] del qual penjaran dos dispositius: edgar i ferran. Cal comentar que aquesta no és una bona estratègia per nomenar els dispositius, habitualment s'usa l'adreça MAC dels dispositius per evitar duplicitat d'identificadors. És important veure també que Asterisk assignarà la contrasenya del dispositiu [ferran] com a 2222, i no pas 1111 (veure referència per detalls de configuració).

Per últim, fixem-nos que en la variable context se li ha assignat LocalSets, que serà el context del dialplan d'on definirem les extensions per comunicar els dispositius.

1.3.2. Configurar els clients de veu ip

Per fer el pas 2 dependrà molt de cada client de veu IP, però essencialment caldrà configurar l'adreça IP de la màquina on es troba la centraleta (o bé el domini, en cas que en tinguem), l'identificador del dispositiu (en el nostre cas: edgar i ferran) i la contrasenya (2222).

Com a clients de veu IP per al PC, o també coneguts com a Softphones, nosaltres hem utilitzat SFLphone VoIP Client (pot trobar-se fàcilment al gestor de paquets Synaptic per Ubuntu) i per a smartphones s'ha utilitzat l'aplicació Zoiper en sistema operatiu Andoid.

Un cop configurats els clients, per veure que la centraleta els detecta com a registrats podem executar "sip show peers" a la terminal d'asterisk. Recordeu a fer un "sip reload" a cada canvi que efectueu a la configuració.

1.3.3. Definir el context i les extensions

El tercer i últim pas consisteix en definir en el dialplan el context que hem assignat als nostres canals. El dialplan es configura en el fitxer /etc/asterisk/extensions.conf. Vegi's l'exemple, que parla per si sol:

[LocalSets]

exten => 100,1,Dial(SIP/ferran) 

exten => 101,1,Dial(SIP/edgar) 

exten => 200,1,Answer()
    same => n,Playback(hello-world)
    same => n,Hangup()

Fixem-nos com hem definit les extensions 100, 101 i 200. [ferran] haurà de marcar l'extensió 101 per trucar l'[edgar]. Per que l'invent funcioní cal fer que asterisk actualitzi el dialplan: executar "dialplan reload" a la terminal.

1.4. Jabber gateway 1: Enviar missatges

La passarel·la Jabber ve donada pel mòdul res_jabber.so, que permet, entre d'altres funcionalitats, associar una extensió a l'enviament d'un missatge de text mitjançant el protocol XMPP a una compta qualsevol que utilitzi el protocol.

Per poder establir la passarel·la cal configurar adequadament el fitxer /etc/asterisk/jabber.conf:

[general]
debug=no
autoprune=no
autoregister=yes
auth_policy=accept

[asterisk]
type=client
serverhost=talk.google.com                   
username=exemple@gmail.com        
secret=contrasenyaexemple
port=5222
usetls=yes
usesasl=yes
status=available
statusmessage="Ohai from Asterisk"

Com es pot observar, a la línia 9 es defineix el servidor de XMPP amb el qual establim la passarel·la i a les dues línies consecutives es declara el compte d'usuari i contrasenya que utilitzarà Asterisk per parlar amb el serverhost.

A continuació cal fer un "reload" de la configuració desde l'intèrpret d'Asterisk:

*CLI> jabber reload
Jabber Reloaded.

Si reproduim les seguents comandes a l'intèrpret hauriem de veure un resultat semblant, a excepció de les dades d'usuari.

*CLI> jabber show connections
Jabber Users and their status:
       User: exemple@gmail.com    - Connected
----
   Number of users: 1

Tot seguit, per associar una extensió a l'enviament d'un missatge caldrà configurar, de nou, el fitxer extensions.conf:

...

exten => 104,1,Answer()
   same => n,JabberSend(asterisk,destinatari.exemple@jabber.es,Acabes de rebre un missatge de ${CALLERID(all)})
   same => n,Hangup()

Si exemple@gmail.com té en el seu llistat d'amics a destinatari.exemple@jabber.es, qualsevol dispositiu que marqui l'extensió 104 provocarà que la centraleta, des del compte exemple@gmail.com envii un missatge XMPP a destinatari.exemple@jabber.es. Per veure la llista d'amistats de exemple@gmail.com podeu executar a la linia de comandes : "jabber show buddies".

1.5. Jabber gateway 2: Rebre missatges

La funció JABBER_RECEIVE() del dialplan ens permet rebre respostes via missatges XMPP, capturar-les i actuar en funció d'aquestes. Típicament utlitizariem ja funció juntament amb JabberSend(), enviant un missatge a algú indicant-li quines respostes són possibles i, en funció del que contesti actuar d'una manera o un altra.

Anem a mostrar un exemple per veure com funciona:

exten => alarma,1,Answer()
   same => n,JabberSend(asterisk,ferranllamas@xat.itic.cat,Ha saltat l'alarma de casa teva, què vols fer?)
   same => n,JabberSend(asterisk,ferranllamas@xat.itic.cat,  1 : si vols trucar a la policia, contesta amb: policia)
   same => n,JabberSend(asterisk,ferranllamas@xat.itic.cat,  2 : si no vols fer res, contesta amb: skip)
   same => n,Set(JabberResponse=${JABBER_RECEIVE(asterisk,ferranllamas@xat.itic.cat)})
   same => n,GotoIf($["${JabberResponse}" = "policia"]?policia,1)
   same => n,GotoIf($["${JabberResponse}" = "skip"]?skip,1)
   same => n,Goto(alarma,1)

exten => skip,1,Answer()
   same => n,JabberSend(asterisk,ferranllamas@xat.itic.cat, Alarma apagada \;))
   same => n,Hangup()


exten => policia,1,Answer()
   same => n,JabberSend(asterisk,ferranllamas@xat.itic.cat, Estem trucant a la policia \;))
   same => n,Hangup()

Com veiem, si desde qualsevol canal registrat es marxa l'extensió "alarma", s'enviarà un missatge XMPP a ferranllamas@xat.itic.cat avisant que s'ha activat l'alarma de casa seva. Automàticament la centraleta s'espera durant un temps determinat a rebre una resposta XMPP de ferranllamas@xat.itic.cat. Si aquest últim respon amb "policia", en un cas real potser hauriem de trucar a la policia però en aquest exemple contestem amb un missatge tranquilitzador. Per altra banda, si contesta amb quelcol diferent a "skip" o "policia", la centraleta torna a enviar el missatge inicial.

A partir d'aquest mecanisme tant simple i, utilitzant més funcionalitats que ofereix aquest mòdul, es podrien pensar utilitats molt interessants.

1.6. Generació Automàtica de trucades

Aquest apartat tracta d'abordar un dels objectius principals del treball. Tal objectiu era que a partir d'un polsador situat en un computador on ni tan sols hi ha un client de veu IP instal·lat es generés una trucada d'emergència a un telèfon determinat. En aquest exemple tenim un polsador però aquest polsador podria representar qualsevol tipus de sensor, moviment, temperatura, etc.

En concret, el muntatge que a continuació es mostra consta de:

Així doncs, la idea és que quan es polsi el polsador, el dispositiu final rebi una trucada i quan despenji escolti una alarma, per exemple, d'incendi.

Per tal objectiu primer cal dominar els mecanismes de trucada automàtics. Nosaltres ens hem centrat en els call files, que són fitxers que contenen accions que la PBX sap interpretar i, si els mous a una carpeta determinada de la mateixa PBX, són executades automàticament. La informació que ens ha servit per entendre aquest muntatge es troba bàsicament en aquesta url : call files. Aquí es pot trobar informació sobre la sintaxi dels call files i exemples més complexos del que es pot arribar a fer.

Nosaltres, però, només ens fa falta un call file tant senzill com el següent alarma.call:

Channel: SIP/ferran
Callerid: ferran
MaxRetries: 1
RetryTime: 10
WaitTime: 20
Context: LocalSets
Extension: 800

En resum, la variable Channel defineix el canal amb el cual es dura a terme l'acció i segueix la mateixa sintaxi que l'aplicació Dial. Es defineixen algunes variables que indiquen de quina forma es farà aquesta trucada i, per últim s'especifica el Context i la Extension a la qual trucarà el Channel.

En el dialplan (extensions.conf) definim aquesta nova extensió:

[LocalSets]

...

exten => 800,1,Answer()
    same => n,Playback(alarm)
    same => n,Hangup()

D'aquesta manera quan es premi el polsador el canal Ferran trucarà a l'extensió 800 i el propi Ferran sentirà el missatge d'alarma.

Per últim, definim l'script truca.sh que executarà remotament el PC-A en la PBX. Té com a paràmetre el nom del call-file que es vol executar:

CALLFILE_DIR=~/callfiles
SPOOL_DIR=/var/spool/asterisk/outgoing/

cp $CALLFILE_DIR/$1.call /tmp/
mv /tmp/$1.call $SPOOL_DIR

Com es pot observar, aquest script és el que provoca la trucada automàtica. Simplement consisteix en moure un call file al directori SPOOL_DIR. És important utilitzar la comanda mv i no pas cp, ja que aquesta última no és una operació atòmica i podria ser que la PBX comencés a executar el call file abans que aquest estigués del tot copiat al directori, la qual cosa podria provocar errors inesperats.

Un cop tenim això configurat, cal escriure el programa que anirà implantat a l'Arduino que al polsar l'interruptor enviarà pel port sèrie el missatge "POLSAT" que haurà d'interpretar el programa que estigui escoltant.

   1 include <avr/interrupt.h>
   2 #include <inttypes.h>
   3 #include <avr/io.h>
   4 #include "serial.h"
   5 #include "queue.h"
   6 #include "polsador.h"
   7 
   8 void click (void){
   9   serial_put('P');
  10   serial_put('O');
  11   serial_put('L');
  12   serial_put('S');
  13   serial_put('A');
  14   serial_put('T');
  15   serial_put('\r');
  16   serial_put('\n');
  17 }
  18 
  19 int main(void){
  20   serial_init();
  21   polsador_init(click);
  22   sei();
  23   PORTB = 0x00;
  24   DDRB = 0xFF;
  25   while(true){;}
  26   return 0;
  27 }

Finalment tenim 'script de Python que correrà en el PC-A, amb l'objectiu de actuar sobre la centraleta. Aquest últim, té per nom truca_boto.py i té la forma següent:

   1 import serial
   2 import subprocess
   3 
   4 ser = serial.Serial('/dev/ttyACM0')
   5 
   6 while True:
   7     if ser.readable():
   8         a = ser.readline()
   9         if (a == "POLSAT\r\n"):
  10             subprocess.call(["ssh", "centraleta@xat.itic.cat", "/home/centraleta/scripts/truca.sh", "alarma"])
  11           
  12 ser.close()

Fixem-nos en que la comanda de la línia número 10 no requereix autentificació. Això és degut a que s'està utilitzant un mecanisme de login amb claus compartides. Per més informació : Documentació Debian sobre login remot amb claus compartides

Per a realitzar el següent projecte necessitarem els següents codis font , exemple1.tar.gz

En la següent imatge veiem com hem de fer les connexions amb l'arduino i la protoboard.

http://fotos.subefotos.com/3cdd44fe72e04713bfc241b7407e2dc4o.png

1.7. Extensió que encèn un LED remot

En aquest nou exemple tractarem d'obrir un LED a distància fent una trucada a una extensió i apagarem el mateix LED fent el mateix procediment una trucada a una altre extensió. Per tant qualsevol canal de la nostre centraleta podrà modificar l'estat d'aquest LED. En aquest exemple estem activant o desactivant un simple LED però aquesta aplicació seria equivalent a obrir els llums o tancar els llums d'un habitatge, calefacció , etc.

En concret, el muntatge que a continuació es mostra consta de:

Primer de tot hem de definir les dues extensions al fitxer /etc/asterisk/extensions.conf per que la centraleta actuí quan rebi una de les dues trucades.

exten => 700,1,Answer()
   same => n,System(echo obre | nc 127.0.0.1 50007)
   same => n,Hangup()

exten => 701,1,Answer()
   same => n,System(echo tenca | nc 127.0.0.1 50007)
   same => n,Hangup()

Com hem dit anteriorment per informar al computador on hi ha el programa python escoltant per un port, per connectar la centraleta asterisk utilitzem l'aplicació netcat. Per realitzar una connexió únicament es requereix una IP i un port, com veiem en aquest exemple la IP de destí és 127.0.0.1 és a dir la pròpia màquina això s'ha fet així ja que la adreça de destí es una IP privada i la centraleta no hi te accès directe , per tant el que hem fet ha sigut un fer un túnel entre les dos màquines executant la següent comanda al computador on hi haurà el servidor python, ssh exemple@exemple.com -R 0.0.0.0:50007:localhost:50007, d'aquesta manera totes les connexions a la màquina exemple@exemple.com al port 50007 aniran redirigides a la màquina que ha executat la comanda.

El següent programa python socket_server.py ens permet estar escoltant pel port 50007 qualsevol connexió des de la centraleta i a la vegada aquesta aplicació esta connectada al port sèrie amb l'arduino per enviar-li una ordre quan aquest rep un missatge de la centraleta.

   1 # Echo server program
   2 import socket
   3 import sys
   4 from select import select
   5 import serial
   6 import subprocess
   7 
   8 def wait_input(llista):
   9     rlist, _, _ = select(llista, [], [])
  10     if rlist[0] == llista[0]:
  11         conn,addr = llista[0].accept()
  12         file_d.append(conn)
  13         print 'Connected by', addr
  14     else:
  15         ordre = rlist[0].recv(1024)
  16         if ordre == "obre\n":
  17             ser.write("E")
  18             del file_d[file_d.index(rlist[0])]
  19         elif ordre == "tenca\n":
  20             ser.write("A")
  21             del file_d[file_d.index(rlist[0])]
  22 
  23 HOST = ''                 # Symbolic name meaning all available interfaces
  24 PORT = 50007              # Arbitrary non-privileged port
  25 ser = serial.Serial('/dev/ttyACM0')
  26 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  27 s.bind((HOST, PORT))
  28 s.listen(1)
  29 file_d=[s]
  30 
  31 while 1:
  32     wait_input(file_d)
  33 
  34 conn.close()
  35 ser.close()

Per altre banda en el nostre microcontrolador tenim el següent programa que únicament esta escoltant pel port sèrie i esperant rebre una E per encendre el led o bé una A per apagar-lo.

   1 #include <avr/interrupt.h>
   2 #include <inttypes.h>
   3 #include <avr/io.h>
   4 #include "serial.h"
   5 #include "queue.h"
   6 
   7 uint8_t lect;
   8 
   9 int main(void){
  10   serial_init();
  11   sei();
  12   PORTB = 0x00;
  13   DDRB = 0xFF;
  14   uint8_t lectura;
  15   while(true){
  16     if (serial_can_read()){
  17       lectura = serial_get();
  18       switch(lectura){
  19       case 'E':
  20         PORTB |= (1<<PB5);
  21         break;
  22       case 'A':
  23         PORTB &= ~(1<<PB5);
  24         break;
  25       default:
  26         break;
  27       }
  28     }
  29   }
  30   return 0;
  31 }

Per realitzar aquest muntatge hem fet servir un led i una resistència com es pot veure a continuació :

http://fotos.subefotos.com/3d9d47d7b4c41dc07e2243747bb0db8bo.png

Per a realitzar el següent projecte necessitarem els següents codis font , exemple2.tar.gz

1.8. Aplicació d'alarma que és pot desactivar

Finalment un cop hem fet per separats els dos exemple anteriors els anem a combinar per així obtenir una aplicació útil, d'aquesta manera el que tindrem serà un polsador que representarà qualsevol tipus de sensor el qual activarà una alarma, a diferència de l'exemple d'alarma anterior aquest encendrà un LED i un Buzzer que representaran l'alarma. Acte seguit és generarà una trucada utilitzant els callfiles explicats anteriorment, per gràcies a això un canal rebrà una trucada amb el missatge d'alarma i a la vegada 3 comptes XMPP rebran un missatge de text per assegurar. Un cop els usuaris siguin conscients de l'activació d'aquesta alarma podran desactivar-la trucant a una extensió, d'aquesta manera es desactivarà l'alarma en el lloc on ha estat activada i a la vegada es tornarà a generar una trucada informant que s'ha desactivat amb èxit.

En concret, el muntatge que a continuació es mostra consta de:

Primer de tot hem de definir les tres extensions al fitxer /etc/asterisk/extensions.conf per que la centraleta actuí quan rebi una de les dues trucades. La primera extensió és la que s'encarregarà d'informar amb un missatge de veu i 3 de text que l'alarma ha estat activada, l'extensió 901 ens servirà doncs per aturar aquesta alarma amb un missatge a traves de sockets, i finalment l'extensió 902 ens indicarà que ha estat desactivada amb total èxit.

exten => 900,1,Answer()
    same => n,JabberSend(asterisk,edgarcosta@xat.itic.cat,"Ha saltat l'alarma de casa teva, truca al 901 per aturar-la")
    same => n,JabberSend(asterisk,ferranllamas@xat.itic.cat,"Ha saltat l'alarma de casa teva, truca al 901 per aturar-la")
    same => n,JabberSend(asterisk,alexcatlla@gmail.com,"Ha saltat l'alarma de casa teva, truca al 901 per aturar-la")
    same => n,JabberSend(asterisk,jomarapa86@gmail.com,"Ha saltat l'alarma de casa teva, truca al 901 per aturar-la")
    same => n,Playback(alarm)
    same => n,Hangup()

exten => 901,1,Answer()
    same => n,System(echo atura_alarma | nc 127.0.0.1 50007)
    same => n,Hangup()

exten => 902,1,Answer()
    same => n,JabberSend(asterisk,edgarcosta@xat.itic.cat,"La alarma ha estat aturada")
    same => n,JabberSend(asterisk,ferranllamas@xat.itic.cat,"La alarma ha estat aturada")
    same => n,JabberSend(asterisk,alexcatlla@gmail.com,"La alarma ha estat aturada")
    same => n,JabberSend(asterisk,jomarapa86@gmail.com,"La alarma ha estat aturada")
    same => n,Playback(alarm-ok)
    same => n,Hangup()

Aquest programa en C es una mica més complex que l'anterior ja que es una combinació, gràcies al mòdul polsador únicament hem de planificar quina acció volem fer quan aquest sigui pressionat , en aquest cas la funció click(),la qual envia un missatge per port sèrie per avisar al computador que l'alarma ha estat activada i alhora activa aquesta alarma tant de llum com sonora. Un cop fet això ens en podem despreocupar i només hem de pensar en atendre els possibles missatges procedents del port sèrie, que es bàsicament el que fa el bucle principal, aquest s'espera ha poder llegir alguna dada pel port sèrie i si aquesta dada es el caràcter 'A' desactiva l'alarma.

   1 #include <avr/interrupt.h>
   2 #include <inttypes.h>
   3 #include <avr/io.h>
   4 #include "polsador.h"
   5 #include "serial.h"
   6 #include "queue.h"
   7 #include "led.h"
   8 #include "buzzer.h"
   9 #include "timer.h"
  10 
  11 void serial_block(uint8_t table[]){
  12   for (int i = 0; table[i]!='\0';i++){
  13     serial_put(table[i]);
  14   }
  15 }
  16 
  17 void click(void){
  18   serial_block("POLSAT\r\n");
  19   led_on();
  20   buzzer_on();
  21 }
  22 
  23 int main(void){
  24   serial_init();
  25   polsador_init(click);
  26   timer_init();
  27   led_init();
  28   buzzer_init();
  29   sei();
  30   PORTB = 0x00;
  31   DDRB = 0xFF;
  32   uint8_t lectura;
  33   while(true){
  34     if (serial_can_read()){
  35       lectura = serial_get();
  36       switch(lectura){
  37       case 'A':
  38         led_off();
  39         buzzer_off();
  40         serial_block("ALARMA_ATURADA\r\n");
  41         break;
  42       default:
  43         break;
  44       }
  45     }
  46   }
  47   return 0;
  48 }

Ara anem a comentar el programa que esta escoltant en el computador, aquest programa python com podem veure també haurà d'escoltar per 2 canals el port sèrie procedent de l'arduino i les connexions socket TCP procedents de la centraleta, poder estar escoltant a varis canals o aconseguim gràcies a la comanda select la qual ens permet bloquejar-nos fins que algun fitxer obert d'escriptura es modificat per més informació python select. Un cop fet això només hem de realitzar l'acció adient, si rebem POLSAT o ALARMA_ATURADA de l'arduino executarem la instrucció que farà la trucada , i si rebem per soquets atura_alarma indicarem a l'arduino que ha d'aturar-la.

   1 # Echo server program
   2 import socket
   3 import sys
   4 from select import select
   5 import serial
   6 import subprocess
   7 
   8 def wait_input(llista):
   9     rlist, _, _ = select(llista, [], [])
  10     if rlist[0] == llista[0]:
  11         conn,addr = llista[0].accept()
  12         file_d.append(conn)
  13         print 'Connected by', addr
  14     elif rlist[0] == llista[1]:
  15          a = ser.readline()
  16          if (a == "POLSAT\r\n"): 
  17              print "polsat"
  18              subprocess.call(["ssh", "centraleta@xat.itic.cat", "/home/centraleta/scripts/truca.sh", "alarma_oberta"])
  19 
  20          elif (a == "ALARMA_ATURADA\r\n"): 
  21              print "alarma aturada"
  22              subprocess.call(["ssh", "centraleta@xat.itic.cat", "/home/centraleta/scripts/truca.sh", "alarma_tancada"])
  23     else:
  24         ordre = rlist[0].recv(1024)
  25         if ordre == "atura_alarma\n":
  26             ser.write("A")
  27             del file_d[file_d.index(rlist[0])]
  28 
  29 
  30 HOST = ''                 # Symbolic name meaning all available interfaces
  31 PORT = 50007              # Arbitrary non-privileged port
  32 ser = serial.Serial('/dev/ttyACM0')
  33 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  34 s.bind((HOST, PORT))
  35 s.listen(1)
  36 file_d=[s,ser]
  37 
  38 while 1:
  39     wait_input(file_d)
  40 
  41 conn.close()
  42 ser.close()

Per realitzar aquest muntatge hem fet servir un led, una resistència i un piezoelèctric com es pot veure a continuació :

http://fotos.subefotos.com/d05d1f7117f17bae5a8172d58b90859bo.png

Per a realitzar el següent projecte necessitarem els següents codis font , exemple3.tar.gz


2023-07-03 11:46