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:
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:
- Instal·lar el servei Asterisk
- Enregistrar usuaris en el servidor i configurar clients de VeuIP
- Fer trucades locals entre dispositius en una mateixa xarxa d'àrea local
- Fer trucades sortint a Internet, comunicant 2 servidors
- Establir la passarel·la entre Asterisk i el protocol XMPP de Jabber
Establir passarel·les entre la xarxa Asterisk i el servei que proporciona GoogleTalk
- Generar trucades automàtiques a partir d'un polsador
- Que la marcació d'una trucada a una extensió concreta encengui un LED remot.
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:
- Quan es rep una trucada entrant per un canal donat
- L'usuari que ha trucat, marca una l'extensió
- Es produeix un salt d'extensions des del Dialplan
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:
- Definició de les extensions corresponents al context, com a una col·lecció d'extensions, definides al Dialplan.
Configuració dels contextos que es defineixen al fitxer corresponent al protocol utilitzat per exemple per el protocol SIP, els contextos estan definits al sip.conf. Les definicions del context, es composa de nom del context entre claus i a continuació les diferents característiques que defineixen el context en particular.
[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:
- [Answer] Despenjar trucades entrants.
- [Hangup] Penja la trucada.
- [Dial] Realitza una trucada segons els paràmetres definits.
- [Playback] Permet reproduir un fitxer d'audio.
[NoOp] No Operation, no fa res.
- [Verbose] Imprimeix el missatge a CLI.
[GoTo] Salta a executar l'aplicació seleccionada.
[GoToif] En funció de l'expressió regular salta a executar l'aplicació pertinent.
[WaitExten] Espera un cert temps.
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:
- Informar a la centraleta dels terminals (canals) que volem comunicar, definir els seus paràmetres (contrasenya, nom d'usuari, etc) i registrar-los.
- Configurar els clients de veu IP per tal que puguin comunicar-se amb la centraleta (Sign In).
- 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:
- Un polsador connectat a un Arduino. L'Arduino envia una notificació pel port sèrie quan és s'ha apretat el polsador.
- Un computador qualsevol on s'hi executa un script en python que escolta el port sèrie. Aquest espera que l'arduino l'informi que el botó s'hagi pitjat i, aleshores, executa una comanda contra la centraleta d'Asterisk que genera una trucada automàtica. (D'ara endevant, li'n direm PC-A)
- La centraleta d'Asterisk que permet la trucada automàtica. (D'ara endavant li direm PBX)
- Canal o dispositiu que rep la trucada automàtica. En concret, serà un terminal mòbil amb un client de veu IP registrat a la centraleta Asterisk amb el protocol SIP. (D'ara endavant li'n direm PC-B)
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:
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.
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:
- Un led connectat a un Arduino. L'Arduino esta esperant pel port sèrie una notificació del computador per modificar l'estat del led.
Un computador qualsevol on s'hi executa un script en python que escolta un port mitjançant un Socket, per més informació http://docs.python.org/2/library/socket.html.Aquest script està esperant que la centraleta li enviï un missatge a través d'una connexió TCP a aquest computador.
- La centraleta d'Asterisk que rep una trucada a una extensió i envia el missatge al computador amb el LED.
- Canal o dispositiu que realitza la trucada. En concret, serà un terminal mòbil amb un client de veu IP registrat a la centraleta Asterisk amb el protocol SIP.
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ó :
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:
- Un led i un piezoelèctric connectats a un Arduino. L'Arduino esta esperant pel port sèrie una notificació del computador per modificar l'estat de l'alarma , o bé estarà esperant que algú polsi el polsador per enviar un missatge al computador i que aquest generi una trucada.
- Un computador que estarà escoltant pel port sèrie instruccions de l'Arduino o bé per sockets instruccions de la centraleta.
- La centraleta d'Asterisk que te la missió de generar aquestes trucades al canal en qüestió i que te la missió d'informar al computador quan s'ha d'aturar l'alarma .
- Canal o dispositiu que realitza la trucada. En concret, serà un terminal mòbil amb un client de veu IP registrat a la centraleta Asterisk amb el protocol SIP.
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ó :
Per a realitzar el següent projecte necessitarem els següents codis font , exemple3.tar.gz