Memoria Pi Mote 3

38
Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) Curso 2012/2013 Pimote Autores: Sara Álvarez Vinagre Alfredo Tendero Casanova Código de la pareja: MT-10 Sistemas Digitales II Universidad Politécnica de Madrid Departamento de Ingeniería Electrónica E.T.S.I. de Telecomunicación

Transcript of Memoria Pi Mote 3

Page 1: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas

Digitales II (SDII)

Curso 2012/2013

Pimote

Autores: Sara Álvarez Vinagre

Alfredo Tendero Casanova

Código de la pareja: MT-10

Sistemas Digitales IIUniversidad Politécnica de Madrid

Departamento de Ingeniería ElectrónicaE.T.S.I. de Telecomunicación

Page 2: Memoria Pi Mote 3

ÍNDICE GENERAL1 INTRODUCCIÓN...................................................................................................................2

2 DIAGRAMA DE SUBSISTEMAS......................................................................................... 3

3 DESCRIPCIÓN DEL SUBSISTEMA HARDWARE............................................................ 4

4 DESCRIPCIÓN DEL SUBSISTEMA SOFTWARE............................................................10

5 PRINCIPALES PROBLEMAS ENCONTRADOS............................................................. 15

6 MANUAL DE USUARIO..................................................................................................... 16

7 BIBLIOGRAFÍA...................................................................................................................17

1

Page 3: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

1 Introducción

1.1 Objetivos del proyecto

El objetivo del proyecto es implementar un mando a distancia universal de aire acondicionado usando una Raspberry Pi, un circuito externo de emisión y recepción de infrarrojos y una aplicación web accesible desde cualquier dispositivo con internet.

La Raspberry se encargará de 'aprender' el funcionamiento de cada mando, almacenándolos y administrándolos de forma que, con la ayuda de la aplicación, desde nuestro smartphone podamos seleccionar entre los distintos modelos disponibles ofreciéndonos una interfaz desde la que controlar el aparato de aire acondicionado.

El sistema es fácilmente extrapolable no sólo a equipos de aire aconcionado, sino cualquier otro cuyo control remoto esté basado en infrarrojos. En general podemos distinguir dos familias dentro de los mandos a distancia: con y sin memoria. Los primeros son a los que nos dedicaremos.

En este tipo de mandos el emisor de la trama no se limita a enviar un código asignado a cada botón, sino que reenvía la configuración completa al dispositivo, independientemente del botón pulsado. Esto entraña un grado de complejidad mayor respecto al mando sin memoria, como puede ser el de una televisión estándar. Sin embargo, aunque en este momento nos centramos en un equipo de aire acondicionado, el sistema pretende poder ser capaz en un futuro de 'aprender' y gestionar controles remotos de ambos tipos.

2

Page 4: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

2 Diagrama de subsistemas

3

Page 5: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

3 Descripción del subsistema Hardware

El hardware está formado por dos nodos, uno operando en la sala de control y otra en la sala donde se encuentra el aire acondicionado.

El nodo en la sala de control es el encargado de recoger la información proporcionada por el usuario (trama, tiempo de bit y frecuencia de portadora) y enviarlo vía radio al nodo de la sala del aire acondicionado, siendo dicha información emitida por un led infrarrojo.

Como novedad respecto al hito 1, con el fin de sustituir el oscilador con NE555 implementado anteriormente para generar la modulación y ampliar la funcionalidad el proyecto, hemos utilizado dos Tulios, plataforma basada en el microcontrolador C1110 de Texas Instruments.

La utilización de este micro nos permite añadir varias mejoras:

• Nodos independientes que se comunican por RF, de modo que el nodo de control (Raspberry Pi) no tiene por qué estar en la misma sala que el aire acondicionado que se quiere controlar.

• Mayor universalidad del proyecto, ya que al generar la modulación desde un timer del microcontrolador podemos ser capaces de generar diferentes frecuencias de portadora (no todos los mandos a distancia utilizan la misma frecuencia).

Las especificaciones del Tulio incluyen:• Comunicación radio en 868MHz • Comunicación con dispositivos externos: UART, SPI, I2C, SMBUS• Conversores analógico digitales• Salidas/entradas de propósito general• Temporizadores internos

4

Page 6: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

3.1 Nodo de sala de control

• Esquema eléctrico. El nodo de control consta de:◦ Una Raspberry Pi en la cual, mediante una interfaz de usuario se podrá seleccionar la

trama a enviar, el tiempo de bit y la frecuencia de portadora. En él correrá un servidor basado en JavaScript que proporcionará una interfaz sencilla desde la que configurar la información a enviar y un botón para enviarla.

◦ Un Tulio, que se comunicará con la Raspberry Pi mediante UART, recogiendo la información indicada por el usuario y enviándola por radio al nodo del aire acondicionado. El código implementado se describe en el apartado de software.

5

Page 7: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013◦ Un display LCD conectado al GPIO de Raspberry que nos será útil a la hora de saber la

temperatura a la que hemos configurado el aire acondicionado y la dirección IP asignada a la Raspberry una vez conectada a la red. La descripción del software que controla lo que se muestra en el display se detallará en el apartado de software.

• Justificación de la solución adoptada. El nodo descrito se encarga únicamente de recoger y retransmitir al nodo del aire acondicionado la información necesaria para reconstruir y emitir la trama que cambia la temperatura en el dispositivo. Por tanto, hemos optado por relegar el trabajo de construcción de la trama al otro nodo, quedando este con una funcionalidad bastante sencilla pero efectiva.

• Problemas encontrados durante la implementación. Dada la fácil implementación de la transmisión radio proporcionada por el fabricante del Tulio, conseguir el correcto funcionamiento de esta parte fue bastante sencillo. La instalación del LCD no fue complicada pero hubo algunas complicaciones al usar la librería software y encontrar los símbolos adecuados.

6

Page 8: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

3.2 Nodo de sala de aire acondicionado

• Esquema eléctrico. En la sala del aire acondicionado habrá un nodo compuesto por:◦ Un Tulio, que recibe por radio la trama que deberá enviar al aire acondicionado.◦ Un circuito emisor, compuesto por un led infrarrojo y un circuito amplificador basado en

un transistor (BC107).

• Justificación de la solución adoptada. Las tramas enviadas por un mando a distancia estándar constan de pulsos modulados (usualmente a 38kHz). Por tanto, nuestro emisor debe ser capaz de modular los bits de la trama antes de ser enviados.

A diferencia del primer hito y tal y como expusimos, hemos sustituido el oscilador hardware basado en NE555 por un temporizador del microcontrolador utilizado (C1110). De este modo el usuario puede configurar fácilmente desde el nodo de control la frecuencia de portadora y el tiempo de bit, permitiendo usar cualquier tipo de mando a distancia.

7

Page 9: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013Por tanto, el circuito externo al microcontrolador es sencillamente un led infrarrojo con un circuito amplificador que se comunica con el aire acondicionado. Mediante el pin P0_0 del Tulio controlamos dicho led.

• Justificaciones teóricas de los valores adoptados para cada uno de los componentes.Para el circuito amplificador hemos utilizado un transistor NPN y una resistencia de 47Ω tal y como se indica a continuación. Este montaje amplifica la señal a la salida del pin para que el led emita con mayor intensidad.

• Problemas encontrados durante la implementación. Los principales problemas vinieron a raíz de que, al estar trabajando con SDCC, no disponíamos de la función μsleep() de C, necesaria para generar el tiempo de bit. Esto nos llevó a plantearnos maneras alternativas, y decidimos usar un timer del Tulio. En primera instancia se utilizó el temporizador nº2, especializado para controlar slots de tiempo. Sin embargo, no fuimos capaces de hacerlo funcionar, y revisando los ficheros de la librería encontramos que el chip de nuestro modelo no soportaba dicho timer.

Otro de los problemas fue decidir cómo controlar el pin de salida de la trama: mediante PWM o haciendo oscilar directamente el pin. Para evitar usar dos temporizadores (uno para generar el tiempo de bit y otro para la frecuencia de portadora), optamos por hacer oscilar directamente el pin. Aquí el problema fue implementar correctamente el código para poder generar, a partir de un solo timer, la frecuencia de portadora y el tiempo de bit.

8

Page 10: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

4 Descripción del subsistema Software

4.1 Proceso del programa principal

• Código implementado

◦ Nodo de la sala de control

▪ scriptLCD.sh

Este es un script en Python encargado de mostrar en el display LCD la información acerca del estado del aire acondicionado así como la IP de la Raspberry. De este modo el usuario puede ver en cualquier momento cuál es el estado del sistema, y conocer a simple vista la IP para conectarse a la interfaz web de usuario. Se encuentra en la carpeta usr/sbin, y es controlado por un daemon de la carpeta init.d. Además está configurado para que arranque con el sistema, de modo que el usuario tenga siempre disponible la información.

#!/usr/bin/python

# The wiring for the LCD is as follows:# 1 : GND# 2 : 5V# 3 : Contrast (0-5V)*# 4 : RS (Register Select)# 5 : R/W (Read Write) - GROUND THIS PIN# 6 : Enable or Strobe# 7 : Data Bit 0 - NOT USED# 8 : Data Bit 1 - NOT USED# 9 : Data Bit 2 - NOT USED# 10: Data Bit 3 - NOT USED# 11: Data Bit 4# 12: Data Bit 5# 13: Data Bit 6# 14: Data Bit 7# 15: LCD Backlight +5V**# 16: LCD Backlight GND

#importimport RPi.GPIO as GPIOimport timefrom datetime import datetimefrom Adafruit_CharLCD import Adafruit_CharLCDfrom subprocess import * from time import sleep, strftime

# Define GPIO to LCD mappingLCD_RS = 7LCD_E = 8LCD_D4 = 25 LCD_D5 = 24LCD_D6 = 23LCD_D7 = 18

# Define some device constants

9

Page 11: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

LCD_WIDTH = 16 # Maximum characters per lineLCD_CHR = TrueLCD_CMD = False

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st lineLCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line

# Timing constantsE_PULSE = 0.00005E_DELAY = 0.00005

def main(): # Main program block GPIO.cleanup() GPIO.setmode(GPIO.BCM) # Use BCM GPIO numbers GPIO.setup(LCD_E, GPIO.OUT) # E GPIO.setup(LCD_RS, GPIO.OUT) # RS GPIO.setup(LCD_D4, GPIO.OUT) # DB4 GPIO.setup(LCD_D5, GPIO.OUT) # DB5 GPIO.setup(LCD_D6, GPIO.OUT) # DB6 GPIO.setup(LCD_D7, GPIO.OUT) # DB7

# Initialise display lcd_init()

# Send some test lcd_byte(LCD_LINE_1, LCD_CMD) lcd_string("P i m o t e") lcd_byte(LCD_LINE_2, LCD_CMD) lcd_string("Alfredo y Sara")

time.sleep(3) # 3 second delay

# Send some text while 1: lcd_byte(LCD_LINE_1, LCD_CMD) # lcd_string(datetime.now().strftime('%b %d %H:%M:%S\n')) cmd2 = "cat /home/pi/pimoteserver/temp.txt" estado = run_cmd(cmd2) lcd_string(estado) lcd_byte(LCD_LINE_2, LCD_CMD) cmd = "ip addr show eth0 | grep inet | awk '{print $2}' | cut -d/ -f1 | tr -d '\n'" ipaddr = run_cmd(cmd) if ipaddr == "": lcd_string("Sin conexion") else: lcd_string('IP %s' % ( ipaddr ))

def run_cmd(cmd): p = Popen(cmd, shell=True, stdout=PIPE) output = p.communicate()[0] return output

def lcd_init(): # Initialise display lcd_byte(0x33,LCD_CMD) lcd_byte(0x32,LCD_CMD) lcd_byte(0x28,LCD_CMD) lcd_byte(0x0C,LCD_CMD) lcd_byte(0x06,LCD_CMD) lcd_byte(0x01,LCD_CMD)

10

Page 12: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

def lcd_string(message): # Send string to display

message = message.ljust(LCD_WIDTH," ")

for i in range(LCD_WIDTH): lcd_byte(ord(message[i]),LCD_CHR)

def lcd_byte(bits, mode): # Send byte to data pins # bits = data # mode = True for character # False for command

GPIO.output(LCD_RS, mode) # RS

# High bits GPIO.output(LCD_D4, False) GPIO.output(LCD_D5, False) GPIO.output(LCD_D6, False) GPIO.output(LCD_D7, False) if bits&0x10==0x10: GPIO.output(LCD_D4, True) if bits&0x20==0x20: GPIO.output(LCD_D5, True) if bits&0x40==0x40: GPIO.output(LCD_D6, True) if bits&0x80==0x80: GPIO.output(LCD_D7, True)

# Toggle 'Enable' pin time.sleep(E_DELAY) GPIO.output(LCD_E, True) time.sleep(E_PULSE) GPIO.output(LCD_E, False) time.sleep(E_DELAY)

# Low bits GPIO.output(LCD_D4, False) GPIO.output(LCD_D5, False) GPIO.output(LCD_D6, False) GPIO.output(LCD_D7, False) if bits&0x01==0x01: GPIO.output(LCD_D4, True) if bits&0x02==0x02: GPIO.output(LCD_D5, True) if bits&0x04==0x04: GPIO.output(LCD_D6, True) if bits&0x08==0x08: GPIO.output(LCD_D7, True)

# Toggle 'Enable' pin time.sleep(E_DELAY) GPIO.output(LCD_E, True) time.sleep(E_PULSE) GPIO.output(LCD_E, False) time.sleep(E_DELAY)

if __name__ == '__main__':

main() GPIO.cleanup()

11

Page 13: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013▪ raspi_wd.sh

Este shell script se encarga de monitorizar el fichero de texto que se actualiza cada vez que el usuario quiere realizar alguna acción sobre el aire acondicionado. Para no acaparar todos los recursos de la máquina con un bucle utiliza la herramienta inotifywait. Se encuentra en la carpeta usr/sbin, y es controlado por un daemon de la carpeta init.d. Además está configurado para que arranque con el sistema, de modo que el sistema esté operativo desde el arranque.

#! /bin/sh### BEGIN INIT INFO# Provides: watchdog del archivo arg.txt# Required-Start: $all# Required-Stop: $all# Default-Start: 2 3 4 5# Default-Stop: 0 1 6# Short-Description: Short script description# Description: Longer script description.### END INIT INFO

case "$1" instart)echo "Iniciando raspi_wd... "while inotifywait -e close_write /home/pi/pimoteserver/arg.txt; do cd /home/pi/enviatramas; PAR=`cat /home/pi/pimoteserver/arg.txt` ; ./raspibase $PAR; done;;stop)echo "Deteniendo raspi_wd..."kill -9 `ps -ef|grep -v grep |grep raspi_wd.sh| awk '{print $2}'`;;*)echo "Modo de empleo: /etc/init.d/raspi_wd.sh {start|stop}"exit 1;;esacexit 0

▪ server.sh

Este shell script tiene la función de arrancar la interfaz de usuario web automáticamente. Se encuentra en la carpeta usr/sbin, y es controlado por un daemon de la carpeta init.d. Además está configurado para que arranque con el sistema, de modo que el servidor esté operativo cuando el dispositivo se enciende.

#! /bin/sh### BEGIN INIT INFO# Provides: pimote server# Required-Start: $all# Required-Stop: $all# Default-Start: 2 3 4 5# Default-Stop: 0 1 6# Short-Description: Short script description# Description: Longer script description.### END INIT INFO

case "$1" instart)echo "Iniciando server Pimote... "cd /home/pi/quiz_editnodejs ./quiz.js &

12

Page 14: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

;;stop)echo "Deteniendo server Pimote..."kill -9 `ps -ef | grep -v grep | grep nodejs | awk '{print $2}'`;;*)echo "Modo de empleo: /etc/init.d/server.sh {start|stop}"exit 1;;esacexit 0

▪ pimote_server.js

Este fichero es el núcleo de la aplicación web programada en NodeJS. Con ella proporcionamos una interfaz de usuario sencilla para que el sistema completo sea cómodo de usar. No incluimos en esta memoria los códigos HTML5 pero en su lugar mostramos la siguiente captura de la aplicación, más representativa de dicho código.

var HTTP = require('http');var URL = require('url');var QS = require('querystring');var MIME = require('mime');var FS = require('fs');

HTTP.createServer(function(request, response) { var MODEL = { show: function (button, action) { FS.readFile('arg.txt','utf-8', function(err, arg) { if (!err) { arg = arg.replace(new RegExp(button + ':.*\n', 'g'), ''); } else { action(err); }; }); FS.readFile('buttons.txt', 'utf-8', function(err, buttons) { if (!err) {

13

Page 15: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

var bt = buttons.match(new RegExp('^'+button+': .*$','m')); var b_t = bt.join().split(": "); FS.writeFile('arg.txt',b_t[1], 'utf-8', function (err) { action(err); }); if(b_t[0] == "OFF"){ FS.writeFile('temp.txt',"Aire apagado", 'utf-8', function (err) { action(err); }); }else{ FS.writeFile('temp.txt',"Encendido " + b_t[0] +"\xDFC", 'ascii', function (err) { //0xDF action(err); }); } } else {action(err); }; }); },

find_bot_arch: function (button, action) { FS.readFile('buttons.txt', 'utf-8', function(err, buttons) { var preg_respuesta = buttons.match(new RegExp('^'+button+': .*$','m')); var preg_y_resp_array = preg_respuesta.join().split(": "); action(err, preg_y_resp_array ); }); },

all_questions: function (action) { FS.readFile('buttons.txt', 'utf-8', function(err, buttons) { action(err, buttons.replace(/^(.*): .*$/mg, '<option>$1</option>')); }); }, delete: function (button, action) { FS.readFile('buttons.txt','utf-8', function(err, buttons) { if (!err) { buttons = buttons.replace(new RegExp(button + ':.*\n', 'g'), ''); FS.writeFile('buttons.txt', buttons, 'utf-8', function (err) { action(err); }); } else { action(err); }; }); }, create: function (button, action) { FS.open('./buttons.txt', 'a', function( e, id ) { FS.write(id, button+'\n', null, 'utf8', function(){ FS.close(id, function(){ console.log('file closed'); }); }); }); var btt = button.split(": "); FS.writeFile('escucha.txt', btt[1], 'utf-8', function (err) { action(err); }); }, update: function (button, action) { FS.readFile('buttons.txt','utf-8', function(err, buttons) { if (!err) { var boton = button.split(":")[0]; buttons = buttons.replace(new RegExp(boton + ':.*\n', 'g'), button + '\n');

14

Page 16: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

FS.writeFile('buttons.txt', buttons, 'utf-8', function (err) { action(err); }); } else { action(err); }; }); } } var VIEW = { render: function (file, r1) { FS.readFile(file, 'utf-8', function(err, data) { if (!err) { var data = data.replace(/<%r1%>/g, r1); if (r1.length==2){ data = data.replace(/<%r2%>/g, r1[0]); data = data.replace(/<%r3%>/, r1[1]); } response.writeHead(200, { 'Content-Type': 'text/html', 'Content-Length': data.length }); response.end(data); } else { VIEW.error(500, "Server operation Error_r"); }; }); },

error: function(code,msg) { response.writeHead(code); response.end(msg);},

file: function(file) { FS.readFile(file, function(err, data) { if (!err) { response.writeHead(200, { 'Content-Type': MIME.lookup(file), 'Content-Length': data.length }); response.end(data); } else { VIEW.error (500, file + " not found"); }; }); } }

var CONTROLLER = { index: function () { MODEL.all_questions (function(err, all_questions) { if (!err) VIEW.render('index.html', all_questions); else VIEW.error(500, "Server bbdd Error_a"); }); }, send: function () { MODEL.all_questions (function(err, all_questions) { if (!err) VIEW.render('show.html', all_questions); else VIEW.error(500, "Server bbdd Error_a"); }); }, new: function () { VIEW.render ('new.html', ""); }, file: function() { VIEW.file(url.pathname.slice(1)); },

edit: function () { MODEL.find_bot_arch(button, function(err, find_bot_arch) { if (!err) VIEW.render('edit.html',find_bot_arch); else VIEW.error(500, "Server bbdd Error_b"); }); },

create: function () {

15

Page 17: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

MODEL.create(button, function(err) { if (!err) CONTROLLER.index(); // redirección a 'GET quiz/index' else VIEW.error(500, "Server bbdd Error_c"); }); }, remove: function() { MODEL.all_questions (function(err, all_questions) { if (!err) VIEW.render('remove.html', all_questions); else VIEW.error(500, "Server bbdd Error_d"); }); }, delete: function () { MODEL.delete (button, function(err) { if (!err) VIEW.render('show.html',find_bot_arch); else VIEW.error(500, "Server bbdd Error_e"); }); },

show: function () { MODEL.show(button, function(err, resp) { if (!err) CONTROLLER.send(); // redirección a 'GET quiz/index' else VIEW.error(500, "Server bbdd Error_b"); }); }, update: function () { MODEL.update(button, function(err) { if (!err) CONTROLLER.index(); // redirección a 'GET quiz/index' else VIEW.error(500, "Server bbdd Error_c"); }); } }

var url = URL.parse(request.url, true); var post_data = ""; request.on('data', function (chunk) { post_data += chunk; }); request.on('end', function() {

post_data = QS.parse(post_data);

// "question" variable global -> visible en controlador button = (post_data.preg || url.query.preg); //question = (post_data.preg + (post_data.resp == null ? "" : ": " + post_data.resp) || url.query.preg); if(post_data.resp != null){ button += ": " + post_data.resp; } var route = (post_data._method || request.method) + ' ' + url.pathname;

switch (route) { case 'GET /index' : CONTROLLER.index() ; break; case 'GET /show' : CONTROLLER.show() ; break; case 'GET /new' : CONTROLLER.new() ; break; case 'GET /edit' : CONTROLLER.edit() ; break; case 'PUT /update' : CONTROLLER.update() ; break; case 'POST /create' : CONTROLLER.create() ; break; case 'GET /remove' : CONTROLLER.remove() ; break; case 'DELETE /delete' : CONTROLLER.delete() ; break; default: { if (request.method == 'GET') CONTROLLER.file() ; else VIEW.error(400, "Unsupported request"); } }

16

Page 18: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

});}).listen(3000);

▪ emisor.c

Este fichero, ejecutado en el Tulio situado en la sala de control, se encarga de recibir la información por la UART y reenviarla vía radio.

Para recibir información por el puerto serie, chequea de manera periódica si hay datos en el pin mediante un buffer circular.

Para emitir datos vía radio utilizamos la funcionalidad aportada por el fabricante, con lo que basta ejecutar la función cc1110_tx(source, destination, data, data_length, type).

Además de todo esto, cabe destacar que el microcontrolador entra en reposo cuando no se están recibiendo datos.

/**********************************************************************************Filename: emisor.c

Description: Codigo para transmitir una trama recibida por UART del nodo base al nodo del a/c.

Comments: El dispositivo envía los datos al nodo del a/c. Después de enviarlo se pone en modo de ahorro de energía. ***********************************************************************************/

#include "hal_main.h"#include "libcc1110.h"

#define TX_ADD 10#define RX_ADD 20#define SLEEP_PERIOD 1#define UART_TX_PERIOD 5 // Sleep Timer period#define UART_BUFFER_SIZE 150 //******** Variables for this appvolatile UINT8 current_232_data = 0; // byte to store rs232 received datavolatile UINT8 current_232_pos = 0; // current position of buffer to store datavolatile UINT8 rs232_buf[UART_BUFFER_SIZE]; // buffer where data is storedvolatile Buffer_cir rs232_buf_cir;

//******** Flags and triggersvolatile UINT8 flag_ST = 0; // Sleep Timer flag volatile UINT8 flag_UART = 0; // Uart RX flag//******** Configuracion RF ********************//estas tres variables siempre deben estar definidas*****//*********************************************UINT8 rf_power = RF_POWER_P10;UINT8 rf_channel = 0x01; // Configuracion del canal RFnetwork_address netAdd; // network address structure

void main(void){

BYTE status = 0;

17

Page 19: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

// Inicialización de leds del TulioINIT_RLED();INIT_GLED();RLED = LED_OFF;GLED = LED_OFF;

/*********************************************************** * Estas dos instrucciones deben estar juntas al principio de

cualquier aplicación de Tulio. */SET_MAIN_CLOCK_SOURCE(CRYSTAL); // Elige el cristal oscilador como

reloj del sistemacc1110_init(); // AWD HW init

enable_wdt (SEC_1); // Configura watchdog con periodo 1 seg

// Configura direccion RFnetAdd.my_address = TX_ADD;

// Al venir de bajo consumo se vuelve a configurar el cristal como reloj del sistema

SET_MAIN_CLOCK_SOURCE(CRYSTAL);

// Configuración de los datos a enviar por RF// Comprueba nuevos datos en la UART con un buffer circulardata_len = buffer_cir_get (&rs232_buf_cir, app_data,

UART_BUFFER_SIZE); if (data_len > 0) {

// Envía datos// cc1110_tx(UINT16 src, UINT16 dst, BYTE* Buffer_add,

BYTE data_len, BYTE com_type)status = cc1110_tx(netAdd.my_address, RX_ADD, app_data,

sizeof(app_data)-6, CO);

// Leds lucen según estado de transmisiónswitch (status) {

case TX_OK:GLED = !GLED;break;

case TX_FAIL:case TX_FAIL_CONFIG:case TX_FAIL_ACK:case TX_FAIL_CHANNEL_BUSY:

RLED = !RLED;break;

}

// Se apaga la radioreset_radio();// Entra en POWER_MODE_2 durante SLEEP_PERIOD segundosset_idle_mode_ST (SLEEP_PERIOD, POWER_MODE_2);

} return;

}

/*==== FUNCIONES PRIVADAS =====================================================*/

//#pragma vector=ST_VECTOR//__interrupt void stIrq(void)void stIrq(void) __interrupt(ST_VECTOR) {

IRCON &= 0x7F;WORIRQ &= 0xFE;SLEEP &= 0xFC;

}

18

Page 20: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

▪ raspi_base.c

Este fichero es ejecutado en la Raspberry Pi, y se encarga de leer la información de un fichero .txt con la información a enviar con el fin de transmitirla a continuación por el puerto serie.

/***********************************************************************************Filename: raspi_base.c

Description: Envía los datos seleccionados por el usuario al Tulio para configurar el aire acondicionado via radio.Important: Advierta que los pines están configurados a 3.3V y las especificaciones de RS232 son para 12V. Si conecta esto a un dispositivo RS232 podría dañar su Raspberry Pi. ***********************************************************************************/

#include <stdio.h>#include <unistd.h> //Used for UART#include <fcntl.h> //Used for UART#include <termios.h> //Used for UART#include <errno.h>

int main(int argc, char *argv[]) { //------------------------- //----- SETUP UART -------- //------------------------- int uart0_filestream = -1; unsigned char tx_buffer[700]; unsigned char *p_tx_buffer; int i; int palabras; struct termios options; unsigned int speed = B115200;

uart0_filestream = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);//Open in non blocking write mode

if (uart0_filestream == -1) { //ERROR - CAN'T OPEN SERIAL PORT perror("Error - Unable to open UART. Ensure it is not in use by another application\n"); }

tcgetattr(uart0_filestream, &options); options.c_cflag = CS8 | CLOCAL | CREAD; //<Set baud rate options.c_iflag = IGNBRK; //IGNPAR | ICRNL; options.c_oflag = 0; options.c_lflag = 0;

cfsetospeed(&options, speed); cfsetispeed(&options, speed); tcflush(uart0_filestream, TCIOFLUSH); tcsetattr(uart0_filestream, TCSANOW, &options);

//----- TX BYTES ----- //tx_buffer[] = {header, header, header, header, header, header,

19

Page 21: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

//words ,tbit1, tbit2(useg), carrier(kHz), data, data,...,end code} // unsigned char buffer[] = {0,0,0,0,0,0, //TX header // 0, 0x02, 0x13, 0x1A, // 0b01010001}; //Trama a enviar

if(argc!=2) {printf("introduzca el nombre del fichero con su extensión.\n"); return 0;

}

FILE *f; unsigned char buffer[700]; int n;

f = fopen(argv[1], "rb"); if (f){ n = fread(buffer, 1, sizeof(buffer), f); printf("n: %i\n", n); } else { printf("ERROR\n"); return 0; }

unsigned char mask = 0x80; unsigned char j = 0;

for (i = 0; i < n; i++) { if (buffer[i] == '1') { tx_buffer[j] |= mask; } else if (buffer[i] == '0') { tx_buffer[j] &= ~mask; } else { break; } mask >>= 1; if (! mask) { j++; mask = 0x80; } } tx_buffer[j++] = '\n';

printf("New tx_buffer: ");

for ( i = 0; i < j; i++) { printf ("0x%x", tx_buffer[i]); }

if (uart0_filestream != -1){ int count = write(uart0_filestream, tx_buffer,j); if (count < 0){

printf("UART TX error\n"); }

} sync(); fflush(stdout); sleep(1);

//----- CLOSE THE UART ----- close(uart0_filestream);

}

20

Page 22: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013◦ Nodo de la sala de aire acondicionado

▪ receptor_ac.c

Este código, ejecutado por el Tulio de la sala de aire acondicionado, es el más complejo de todos los expuestos, puesto que es el que genera la trama modulada en uno de sus pines.

Para empezar, un buffer circular chequea si llega nueva información vía radio.

buffer_cir_get (&buf_rx, radio_buf, BUFFER_SIZE)

Cuando esto ocurre, se recorre el array radio_buf y se guarda en diferentes variables la información correspondiente a tiempo de bit y de portadora. Con ellos se configura el timer 3 (ajustado a frecuencia de portadora) y se calcula el número de iteraciones del mismo que conforman un tiempo de bit (variable veces).

A continuación, un bucle for recorre sólo los elementos de radio_buf que corresponden a la trama que se debe enviar al aire acondicionado. Dentro de dicho bucle se recorre cada bit de la trama con una máscara, encendiendo o apagando el pin P0_0 en función de su valor (0 o 1).

Para asegurarnos de que el tiempo de bit es el adecuado, se comprueba mediante el bucle while(counter < veces) que se han producido el número de iteraciones del timer 3 necesarias para que haya transcurrido el tiempo de bit. Para contar las iteraciones se comprueba el valor del registro TIMIF, en el cual el bit 0 corresponde al flag de overflow del timer 3.

/**********************************************************************************Filename: receptor_ac.c

Description: Código de prueba para transmitir una trama recibida por radio desde el nodo base al a/c.

Comments: El dispositivo recibe los datos del nodo base y los envía al a/c. Después de enviarlo se pone en modo de ahorro de energía.

***********************************************************************************/

#include "hal_main.h"#include "libcc1110.h"#include "hal_timer.h"#include "hal_defines.h"

#define LED_TOGGLE() \ do { \ GLED = LED_ON; \ RLED = LED_ON; \ halWait(200); \ GLED = LED_OFF; \ RLED = LED_OFF; \ } while (0)

#define PORT_0 0#define PORT_1 1#define PORT_2 2

21

Page 23: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

#define PULL_UP 0#define PULL_DOWN 1

#define RISING_EDGE 0#define FALLING_EDGE 1

#define TX_ADD 10#define RX_ADD 20

//******** Configuracion RF ********************//estas tres variables siempre deben estar definidas*****//*********************************************UINT8 rf_power = RF_POWER_P10; // TX Power OutputUINT8 rf_channel = 0x01; // RF channel configurationnetwork_address netAdd; // network address structure

//********Buffer circular RF********************//*********************************************extern Buffer_cir buf_rx;

voidmain(void){

//Tiempo de bit en useg UINT32 tbit = 0;

//Periodo de portadora en miliseg. UINT8 tcarrier = 0;

//El periodo fijado en el timer debe ser la mitad que tcarrier UINT8 tadaptada = 0;

UINT8 veces = 0; UINT8 counter = 0;

UINT8 index = 0; UINT8 mask;

/***Rx de datos***/

//Radio Buffer (ver includes para más información) UINT8 radio_buf[BUFFER_SIZE]; //direccion UINT16 dst_add = 0; // Configura direccion de la fuente netAdd.my_address = RX_ADD;

// Inicialización de leds del Tulio INIT_RLED(); INIT_GLED(); RLED = LED_OFF; GLED = LED_OFF;

/*********************************************************** * Estas dos instrucciones deben estar juntas al principio de

cualquier aplicación de Tulio. */

SET_MAIN_CLOCK_SOURCE(CRYSTAL); // Elige el cristal oscilador como reloj del sistema cc1110_init(); // AWD HW init

// Radio como receptor set_rx();

// Bucle principal while (1) { // chequea nueva información por radio en buffer circular

22

Page 24: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

while (buffer_cir_get (&buf_rx, radio_buf, BUFFER_SIZE) > 0) {

// Comprueba si direccion de destino es mi direccion. Evita procesar mensajes enviados a otros destinatarios. dst_add = (radio_buf[4] << 8) + radio_buf[5]; if (dst_add == RX_ADD) {

LED_TOGGLE(); //P0_0 como pin de salida

IO_DIR_PORT_PIN(0,0,IO_OUT);

//Configuración del timer 3 para generar portadora tcarrier = radio_buf[9]; tadaptada = tcarrier/2;

Module_Running_Timer_34 (tadaptada, 3); TIMER3_RUN(TRUE); tbit = (radio_buf[7] << 8) + radio_buf[8]; //Número de vueltas del timer 3 que conforman el tiempo de bit veces = tbit/tadaptada; for (index = 10; index < 10+radio_buf[6]; index++) {

for (mask=0x80; mask; mask >>=1) { counter = 0x00; P0_0 = 0; if(mask & radio_buf[index]){ while(counter < veces){ P0_0 ^= (!!mask); while(!(TIMIF&0x01)){} counter++; TIMIF &= 0xFE; //Limpia flag } }else{ while(counter < veces){ P0_0 = 0; while(!(TIMIF&0x01)){} counter++; TIMIF &= 0xFE; //Limpia flag } } } } } } TIMER34_INIT(3); } }

/*==== FUNCIONES PRIVADAS =====================================================*///#pragma vector=URX0_VECTOR//__interrupt void urx0_isr (void)void urx0_isr (void) __interrupt(URX0_VECTOR){}

• Justificación de la solución adoptada

Tal y como se explicó anteriormente, generar la trama mediante un temporizador del microcontrolador fue la alternativa ante la imposibilidad de utilizar la función μsleep(), necesaria para generar el tiempo de bit, además de servir para eliminar el oscilador hardware anteriormente utilizado. Además se ha procurado usar un solo temporizador en lugar de dos

23

Page 25: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013para generar tanto la oscilación de la trama modulada como el tiempo de bit, con el fin de no sobrecargar el micro.

• Plan de pruebas

Para comprobar el correcto funcionamiento del código implementado, fijamos una trama de prueba y su correspondiente tiempo de bit y frecuencia de portadora. Los datos son:

◦ Trama: 01010000 01100101 01101110 01100101

◦ Tiempo de bit teórico: 531 μs

◦ Frecuencia de portadora: 1/26 μs => 38 kHz

A la salida del pin del sistema Tulio tenemos la siguiente trama:

En la segunda captura vemos en detalle uno de los bits a la salida del pin. En el se puede comprobar que la frecuencia de portadora y el tiempo de bit se aproximan a los calculados teóricamente.

24

Page 26: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

Comparando, a la salida del receptor de infrarrojos obtenemos la siguiente trama demodulada:

25

Page 27: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

5 Principales problemas encontrados

Tal y como se ha venido recalcando a lo largo de la memoria, los principales problemas han sido a raíz de la falta de documentación acerca del aire acondicionado estudiado. Esto nos ha llevado a destinar mayor tiempo del que se esperaba a buscar información al respecto y probar a 'descifrar' el funcionamiento del mando. Sin embargo, una vez pasado este escollo nos hemos desenvuelto con normalidad, y esperamos que en el futuro la información recabada hasta ahora nos permita continuar con el proyecto con mayor facilidad.

Además, en el segundo hito, nos encontramos con ciertas complicaciones relacionadas con el manejo de los CC1100 tales como las limitaciones con respecto a los timers anteriormente comentadas y el protocolo de comunicación vía radio, que añadía bit espúreos al final de nuestra trama.

Finalmente, el tercer hito se ha caracterizado por la mejora de la interfaz de usuario, que mediante un sistema de ficheros actualizables permite una comunicación tranparente con el aire acondicionado. Hacer que esto funcionase de manera estable y sin fallos llevó algo de tiempo, pues fue necesario hacer uso de expresiones regulares integradas en el servidor node.js para poder leer y procesar adecuadamente los parámetros fijados por el usuario.

Por otro lado, uno de los logros que queríamos conseguir en este hito era el aprendizaje de tramas. Tal y como se expuso en el hito 2, el código para detectar eventos y muestrear la señal de entrada procedente de un led infrarrojo ya lo teníamos implementado para entonces. El código Python es el siguiente:

#!/usr/bin/pythonimport sysimport RPi.GPIO as GPIOimport timeitimport timeGPIO.setmode(GPIO.BCM)

#setup pin as inputs and put the pull down resistor on it to avoid any issuesGPIO.setup(11, GPIO.IN, pull_up_down=GPIO.PUD_UP) #Raw photodiode GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_UP) #IR decoder

#create list to store datadata = []#create list to store bit timetbit = []#create list to store carrier periodtcarrier = []

# int t_bit_start# int t_bit_end# int t_bit_min# int t_carrier_start# int t_carrier_end# int t_carrier_min# int diff_tbit# int diff_tcarrier# int cociente

#the function called by the interrupt on pin 17def capture_data(channel):

26

Page 28: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

if GPIO.input(channel): print('Input was HIGH') data.append(0)

t_bit_start = timeit.timeit() #check start time

while GPIO.input(channel) == GPIO.HIGH:pass #wait

t_bit_end = timeit.timeit() #check time at the end diff_tbit = t_bit_end - t_bit_start print('Bit time: '+diff_tbit) tbit.append(diff_tbit)

else: print('Input was LOW') data.append(1)

t_bit_start = timeit.timeit() #check start time while GPIO.input(channel) == GPIO.LOW: pass #wait t_bit_end = timeit.timeit() #check time at the end diff_tbit = t_bit_end - t_bit_start print('Bit time: '+diff_tbit) tbit.append(diff_tbit)

# def capture_data(channel): # if GPIO.input(channel):# print('Input was HIGH')# data.append(1)# t_carrier_start = timeit.timeit() #check time at beginning# GPIO.add_event_detect(22, GPIO.RISING, callback=capture_tcarrier)# t_carrier_start = timeit.timeit() #reset start time for next cycle

# else:# print('Input was LOW')# data.append(0)

# t_bit_end = timeit.timeit() #check time at the end# diff_tbit = t_bit_end - t_bit_start# print('Bit time: '+diff_tbit)# tbit.append(diff_tbit)# t_bit_start = timeit.timeit() #reset start time for next cycle

#the function called by the interrupt on pin 18def capture_tcarrier(channel):

t_carrier_start = timeit.timeit() #check start time while GPIO.input(channel) == GPIO.HIGH: pass #wait t_carrier_end = timeit.timeit() #check time at the end diff_tcarrier = 2*(t_bit_end - t_bit_start) print('Carrier period: '+diff_tcarrier) tcarrier.append(diff_tcarrier)

#initialize the interrupt

print('Detecting signal')

try:print('entro')GPIO.add_event_detect(11, GPIO.RISING, callback=capture_data)GPIO.add_event_detect(12, GPIO.RISING, callback=capture_tcarrier)print('por aqui ando')

except KeyboardInterrupt:

27

Page 29: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

print('key interrupt') GPIO.cleanup()

t_bit_min = 0 t_carrier_min = 0

for t in tbit: t_bit_min = 0 if t<t_bit_min: t_bit_min = t

for t in tcarrier: t_carrier_min = 0 if t<t_carrier_min: t_carrier_min = t

# Write mode creates a new file or overwrites the existing content of the file. # Write mode will _always_ destroy the existing contents of a file. try: # This will create a new file or **overwrite an existing file**. f = open("buttons.txt", "w") try: tbitstring = str(t_bit_min) #stringify tbit tcarrstring = str(t_carrier_min) #stringify tcarrier f.write(tbitstring) f.write(tcarrstring) for index,item in enumerate(data): cociente = tbit(index)/t_bit_min cocienteint = int(cociente) #force cociente to int for x in range(0,cocienteint): itemstring = str(item) #stringify item f.write(itemstring) # Write bit value to file #f.writelines(lines) # Write a sequence of strings to a file f.write('\n') finally: f.close()

print('Button added') sys.exit() except IOError: print('ERROR while reading file')

Sin embargo al hacer pruebas nos topamos con que al ser un programa en espacio de usuario, no tenía la suficiente rapidez para capturar todas las interrupciones al verse interrumpido por otros procesos del sistema. Por tanto nos dispusimos a la opción más fiable en cuanto a lo que tiempo y rapidez de atención se refiere: hacer un driver.

Para la realización de dicho driver utilizamos ampliamente el libro de referencia Linux Device Drivers de la editorial O'Reilly, puesto que era la primera vez que nos enfrentabamos a una tarea similar. El principal problema a la hora de implementar el driver estaba en que los fabricantes de Raspberry Pi no dan soporte para ciertas funcionalidades como por ejemplo el muestreo del GPIO a nivel hardware. Es decir, se puede implementar, pero no está documentado en detalle.

El primer escollo a superar fue encontrar los valores de los registros para poder escribir y leer de ellos, así como la manera de hacerlo.

Finalmente, el driver que hemos desarrollado es capaz de muestrear la señal entrante de por un pin, y almacenarlo en un buffer que será posteriormente pasado al espacio de usuario para ser interpretado.

El código es el que se detalla a continuación:

28

Page 30: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

/* * PIMOTE driver */

#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/io.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/gpio.h>

#define MAJOR_NUMBER 70

//Function Select Registers//FSELn = 000 --> Pin n is an input//FSELn = 001 --> Pin n is an output#define GPFSEL0 0x20200000#define GPFSEL1 0x20200004#define GPFSEL2 0x20200008#define GPFSEL3 0x2020000C#define GPFSEL4 0x20200010#define GPFSEL5 0x20200014

//Pin Level Registers//LEVn = 0 --> Pin n is low//LEVn = 1 --> Pin n is high#define GPLEV0 0x20200034#define GPLEV1 0x20200038

//Pin Output Set Registers//SETn = 0 --> No effect//SETn = 1 --> Set GPIO pin n#define GPSET0 0x2020001C#define GPSET1 0x20200020

//Pin Output Clear Registers//CLRn = 0 --> No effect//CLRn = 1 --> Clear GPIO pin n#define GPCLR0 0x20200028#define GPCLR1 0x2020002C

//Event Detect Status Registers//EDSn = 0 --> Event not detected on pin n//EDSn = 1 --> Event detected on pin n#define GPEDS0 0x20200040#define GPEDS1 0x20200044

//Rising Edge Detect Enable Registers//RENn = 0 --> Rising edge detect disabled on pin n//RENn = 1 --> Rising edge detect enabled on pin n (sets corresponding bit in EDSn)#define GPREN0 0x2020004C#define GPREN1 0x20200050

//Falling Edge Detect Enable Registers//FENn = 0 --> Falling edge detect disabled on pin n//FENn = 1 --> Falling edge detect enabled on pin n (sets corresponding bit in EDSn)#define GPFEN0 0x20200058#define GPFEN1 0x2020005C

#define GPIO_INT0 49#define GPIO_INT1 50

29

Page 31: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

ssize_t pimote_read (struct file *filep, char *buf, size_t count, loff_t *f_pos);ssize_t pimote_write (struct file *filep, const char *buf, size_t count, loff_t *f_pos);int pimote_open (struct inode *inode, struct file *filp);int pimote_close (struct inode *inode, struct file *filp);

static struct file_operations pimote_fops = {//.owner = THIS_MODULE,.read = pimote_read,.write = pimote_write,.open = pimote_open,.release = pimote_close,

};

char data_buffer [512]; //Buffer to collect datachar t_data_buffer [521]; //Buffer to collect data timeschar t_carrier_buffer [512]; //Buffer to collect tcarrierint data_pos; //Position of data_buffer to put dataint t_data_pos; //Position of t_data_buffer to put timeint t_carrier_pos; //Position of time_buffer to put timeint finish; //Flag for finished captureint irq_17; //Set interrupt for GPIO pin 17

irq_handler_t capture_data(int irq, void *dev_id, struct pt_regs *regs){

char* ptr;unsigned int mask;int data_value;int event_detect;

printk("He llegado a capture_data");

if (! request_mem_region(GPFSEL0, 0x40, "GPFSEL0")) { printk(KERN_INFO "CAPTURE_DATA: Error request_mem_region"); //return 1; }

//Point GPFSEL0 ptr = ioremap_nocache(GPFSEL0, 0x40);

if (ptr) {printk(KERN_INFO "CAPTURE_DATA: remap 0x%X to 0x%X\n", GPFSEL0, (unsigned

int)ptr);} else {

printk(KERN_INFO "CAPTURE_DATA: Can't remap 0x%X\n", GPFSEL0);//return 1;

}

//Leer y limpiar GPEDS0. Imprimir por pantalla. mask = ioread32(ptr+0x40); //Read GPFSEL0 value printk(KERN_INFO "CAPTURE_DATA: GPSED0 antes de limpiar = %x\n",mask);

iowrite32(mask,ptr+0x40);mask = ioread32(ptr+0x40); //Read GPFSEL0 valueprintk(KERN_INFO "CAPTURE_DATA: GPSED0 despues de limpiar = %x\n",mask);

finish = 1;

//Read event detect statusevent_detect = ioread32(ptr+0x34); //Read GPLEV0 valueevent_detect &= 0x00020000; //0000 0000 0000 0010 0000 0000 0000 0000 if(event_detect == 0x00020000) {

//Level is highdata_value = 1;

} else {//Level is lowdata_value = 0;

30

Page 32: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

}

//Save data_value in data_bufferdata_buffer[data_pos] = data_value;data_pos++;

iounmap(ptr);release_mem_region(GPFSEL0, 0x40);return IRQ_HANDLED;

}

int pimote_init (void) {int result;unsigned int mask;char* ptr;unsigned int irq_flags_17;

//Register device result = register_chrdev (MAJOR_NUMBER, "aprende", &pimote_fops);

if (result < 0) { printk(KERN_ALERT "PIMOTE not registered as char device\n"); }

printk(KERN_INFO "PIMOTE registered as char device\n");

//Request memory if (! request_mem_region(GPFSEL0, 0x40, "GPFSEL0")) { printk(KERN_INFO "PIMOTE_INIT: Error request_mem_region"); } //Point GPFSEL0 ptr = ioremap_nocache(GPFSEL0, 0x40);

if (ptr) {printk(KERN_INFO "PIMOTE_INIT: remap 0x%X to 0x%X\n", GPFSEL0, (unsigned

int)ptr);} else {

printk(KERN_INFO "PIMOTE_INIT: Can't remap 0x%X\n", GPFSEL0);}

mask = ioread32(ptr); //Read GPFSEL0 valuemask &= 0xFFFF8FFF; //Pin 4: bits 12 to 14 = 000 on GPFSEL0iowrite32(mask,ptr); //Set pin 4 as input

mask = ioread32(ptr+0x04); //Read GPFSEL1 valuemask &= 0xFF1FFFFF; //Pin 17 bits 21 to 23 on GPFSEL1iowrite32(mask,ptr+0x04); //Set pin 17 as input

//Rising edge detect enable on pin 4 //Bit 4 = 1 on GPREN0//GPREN0 |= 0x00000010;mask = ioread32(ptr+0x4C); //Read GPREN0 value mask |= 0x00000010; //Pin 4: bit 4 = 1 on GPREN0iowrite32(mask,ptr+0x4C); //Write on GPREN0

//Falling edge detect enable on pin 17//Bit 17 = 1 on GPFEN0//GPFEN0 |= 0x00020000;mask = ioread32(ptr+0x58); //Read GPFEN0 valuemask |= 0x00020000; //Pin 17: bit 17 = 1 on GPFEN0iowrite32(mask,ptr+0x58); //Write on GPFEN0

//Rising edge detect enable on pin 17//Bit 17 = 1 on GPREN0//GPREN0 |= 0x00020000;mask = ioread32(ptr+0x4C); //Read GPREN0 valuemask |= 0x00020000; //Pin 17: bit 17 = 1 on GPREN0iowrite32(mask,ptr+0x4C); //Write on GPREN0

31

Page 33: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

/* request_irq: allocate a given interrupt line *///Interrupt from raw photodiode (pin 4)//Prueba esto, y si no prueba con irq_flags = 0;

//Leer y limpiar GPEDS0. Imprimir por pantalla. mask = ioread32(ptr+0x40); //Read GPFSEL0 value printk(KERN_INFO "PIMOTE_INIT: GPSED0 antes de limpiar = %x\n",mask);

iowrite32(mask, ptr+0x40); mask = ioread32(ptr+0x40); //Read GPFSEL0 valueprintk(KERN_INFO "PIMOTE_INIT: GPSED0 despues de limpiar = %x\n",mask);

iounmap(ptr);release_mem_region(GPFSEL0, 0x40);

irq_17=gpio_to_irq(17); printk(KERN_INFO "irq_17: %d\n",irq_17);

irq_flags_17 = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;

finish = 0;if(request_irq(irq_17, (irq_handler_t) capture_data, irq_flags_17,

"capture_data", NULL)){printk(KERN_INFO "PIMOTE_READ: request_irq failed: pin 17");

}

printk(KERN_INFO "He terminado el init\n"); return 0;}

void pimote_exit (void){

char* ptr;int i = 0;

if (! request_mem_region(GPFSEL0, 0x40, "GPFSEL0")) { printk(KERN_INFO "PIMOTE_EXIT: Error request_mem_region"); }

//Point GPFSEL0 ptr = ioremap_nocache(GPFSEL0, 0x40);

if (ptr) {printk(KERN_INFO "PIMOTE_EXIT: remap 0x%X to 0x%X\n", GPFSEL0, (unsigned

int)ptr);} else {

printk(KERN_INFO "PIMOTE_EXIT: Can't remap 0x%X\n", GPFSEL0);}

free_irq(irq_17, NULL);

printk(KERN_INFO "PIMOTE_EXIT: Trama capturada \n");

for(i = 0; i < 512; i++) { printk("%d ", data_buffer[i]);

}

unregister_chrdev (MAJOR_NUMBER, "aprende"); printk(KERN_INFO "PIMOTE unregistered\n");

iounmap(ptr);release_mem_region(GPFSEL0, 0x40);

}

int pimote_open(struct inode *inode, struct file *filp){

// 4 --> Raw Photodiode (12 to 14 bits on GPFSEL0 = 000)

32

Page 34: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

// 17 --> IR demod. (21 to 23 bits on GPFSEL1 = 000)

char* ptr;//unsigned int mask;//unsigned int irq_flags_17;

printk(KERN_INFO "Open Pimote driver...");

if (! request_mem_region(GPFSEL0, 0x40, "GPFSEL0")) { printk(KERN_INFO "PIMOTE_OPEN: Error request_mem_region"); }

//Point GPFSEL0 ptr = ioremap_nocache(GPFSEL0, 0x40);

if (ptr) {printk(KERN_INFO "PIMOTE_OPEN: remap 0x%X to 0x%X\n", GPFSEL0, (unsigned

int)ptr);} else {

printk(KERN_INFO "PIMOTE_OPEN: Can't remap 0x%X\n", GPFSEL0);}

iounmap(ptr);release_mem_region(GPFSEL0, 0x40);

return 0;}

ssize_t pimote_read(struct file *filep, char *buf, size_t count, loff_t *f_pos){

char* ptr;

printk(KERN_INFO "Read Pimote driver...");

if (! request_mem_region(GPFSEL0, 0x40, "GPFSEL0")) { printk(KERN_INFO "PIMOTE_READ: Error request_mem_region"); }

//Point GPFSEL0 ptr = ioremap_nocache(GPFSEL0, 0x40);

if (ptr) {printk(KERN_INFO "PIMOTE_READ: remap 0x%X to 0x%X\n", GPFSEL0, (unsigned

int)ptr);} else {

printk(KERN_INFO "PIMOTE_READ: Can't remap 0x%X\n", GPFSEL0);}

iounmap(ptr);release_mem_region(GPFSEL0, 0x40);return 0;

}

ssize_t pimote_write(struct file *filep, const char *buf, size_t count, loff_t *f_pos){

//int mask;//int* ptr;printk(KERN_INFO "PIMOTE_WRITE: Nothing to be done.\n");

return 0;}

int pimote_close(struct inode *inode, struct file *filp){

char* ptr;printk(KERN_INFO "Closing Pimote driver...");

33

Page 35: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

if (! request_mem_region(GPFSEL0, 0x40, "GPFSEL0")) { printk(KERN_INFO "PIMOTE_CLOSE: Error request_mem_region"); }

//Point GPFSEL0 ptr = ioremap_nocache(GPFSEL0, 0x40);

if (ptr) {printk(KERN_INFO "PIMOTE_CLOSE: remap 0x%X to 0x%X\n", GPFSEL0, (unsigned

int)ptr);} else {

printk(KERN_INFO "PIMOTE_CLOSE: Can't remap 0x%X\n", GPFSEL0);}

copy_to_user(data_buffer);

iounmap(ptr);release_mem_region(GPFSEL0, 0x40);return 0;

}

module_init(pimote_init);module_exit(pimote_exit);

MODULE_DESCRIPTION("Pimote GPIO driver");MODULE_AUTHOR("Alfredo & Sara");MODULE_LICENSE("GPL");

34

Page 36: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

6 Manual de usuarioPara controlar el sistema implementado hasta ahora deberemos seguir los siguientes pasos:

•En primer lugar encendemos la Raspberry Pi conectándola a la corriente a través del puerto micro-USB y alimentamos los Tulios conectándolos a un equipo/Raspberry a través del puerto USB.

•En el sistema receptor, además del Tulio, debemos alimentar el circuito que contiene el led infrarrojo que le envía la trama al aire acondicionado. Para ello conectamos los pines de alimentación y masa del Tulio a la placa de inserción y el pin0 a la entrada del circuito amplificador. (Comprobamos que el led verde esta encendido, si esto no ocurriese, comprobar las conexiones entre la placa y el Tulio)

•Una vez alimentados nos conectamos al servidor de la Raspberry para acceder a la interfaz de usuario del sistema. En un navegador introducimos la dirección IP asignada a la Raspberry seguido del puerto 3000 y “/index” que nos llevará a la página principal

host:3000/index

donde “host” es la dirección IP asignada al dispositivo (Esta dirección aparecerá reflejada en el display)

35

Page 37: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

•Una vez en el servidor seleccionamos lo que queremos enviar en el spinner disponible en la página principal y pulsamos el botón enviar. La temperatura enviada aparecerá en el display y se habrá enviado

•Adicionalmente, si quiere guardar una nueva trama, deberá introducir el fichero de texto con la información de la trama en la carpeta “enviatramas” situada en el directorio “home/pi/” de Raspberry y después pulsar en “guardar nueva trama” en el servidor, donde nos aparecerá una sencilla interfaz en la que introducir el nombre del botón (en mayúsculas) y el nombre del fichero .txt al que hace referencia dicho botón (Para ello es importante mantener la extensión del fichero cuando guardemos el botón). Por ejemplo:

OFF: tramaoff.txt

Tras esto pulsamos el botón para guardarlo y ya dispondremos del nuevo botón en el spinner de la página principal.

•Análogamente dispone de una interfaz para editar o borrar botones de la lista de botones disponibles.

36

Page 38: Memoria Pi Mote 3

Memoria del proyecto desarrollado en Sistemas Digitales II (SDII) curso 2012/2013

7 Bibliografía

• Información sobre drivers en Linux:Third Edition of Linux Device Drivers, by Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartma.

• LIRC GPIO driver:http://aron.ws/projects/lirc_rpi/

• Información sobre los pines del GPIO:http://elinux.org/RPi_Low-level_peripherals

• Agradecimientos a Guy Palmer.• Datasheet del CC1110 por Texas Instruments• Agradecimientos al equipo de Temp-LSI

37