Calculadora on-line de timers del PIC

Electrónica Sin comentarios

Hace tiempo hice una calculadora para mi uso interno la cual calculaba los valores que había que poner a un timer de un microcontrolador PIC (con su preescaler, su frecuencia, etc) para llegar a un retardo determinado, o viceversa, dado un valor del timer calcular qué retardo provoca. Es muy sencilla y está hecha integramente en html y javascript:

http://www.sistemasorp.es/blog/temporizadores_pic.html

Me he cambiado de hosting

Sin categoría Sin comentarios

Después de 5 años en Hostony he decidido cambiar de hosting ya que últimamente estába teniendo muchos problemas (errores del servidor, cambio de un día para otro la configuración provocando que algunos PHPs no se pudieran ver, me quitaban el acceso ssh después de haberselo pedido, hora desfasada, etc). Como en España los hosting siguen siendo caros para los servicios que ofrecen he optado por otro hosting americano llamado Bluehost, a ver que tal me va.

Sistema de riego automático

Electrónica, Proyectos 50 Comentarios

Muchas veces me ha surgido la necesidad de regar las plantas que tenía en casa mientras estaba fuera unos días. Por desgracia algunas de ellas han perecido por no ser regadas cada poco tiempo.

De esa necesidad surge este proyecto y para llevarlo a cabo he usado una bomba de agua, un bidón, un detector de nivel y una placa basada en un pic 16f628 con un relé.

La bomba de agua puede ser la que se usa para renovar el agua de una pecera o para mantener el chorro de una pequeña fuente. Sólo hay que conectarla a la alimentación de la casa para que empiece a bombear agua. Es muy importante que cuando esté enchufada tenga siempre agua, porque si no el motor se puede llegar a quemar. Por otro lado la altura de la manguera que va desde la bomba hasta el tiesto no puede ser mayor de un metro, ya que estos motores van perdiendo fuerza de bombeo a medida que crece la altura por donde tienen que enviar el agua.

El bidón que he usado en el proyecto es uno de los de agua mineral de 5 litros de cualquier marca. Al ser de plástico es fácilmente manejable para poder hacer un agujero y meter el detector de nivel.

El detector de nivel es necesario para evitar que la bomba siga extrayendo agua y se queme el motor por no tener agua que bombear. Yo he usado el de sure electronics.

La placa gobernada por un pic 16f628a es la que activará o desactivará la bomba de agua mediante un relé. Puede ser configurado mediante jumpers tanto el tiempo de riego como el intervalo. También se puede activar el riego manualmente mediante un botón.

Los componentes de la placa que he usado son:

  • Microcontrolador PIC 16F628A
  • 2 diodos led
  • 2 resistencias de 220 ohm.
  • 1 resistencia de 68K ohm.
  • 1 transistor BC237
  • 1 diodo 1N4148
  • Un relé de 12 voltios RA12W-K
  • Un botón
  • Un regulador 7805
  • Un condensador de 1000 uf
  • Un condensador de 100 uf
  • 2 bornes de 2 tomas y uno de 3 tomas.
  • Pines y jumpers
  • Fuente de alimentación de 12 V. y 0,5A.

Es importante que las pistas (o los cables de una placa de topos) que van a los contactos del relé sean más anchos para soportar la intensidad de la corriente.

Para no tener que pelar el cable de la bomba de agua, he usado un alargador que será al que se enchufe la bomba, pero que está cortado por uno de sus cables para poder engancharlo al relé:

El código fuente del PIC:

main.h

#include <16F628A.h>
 
#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
 
#use delay(clock=4000000)
#use fast_io(A)
#use fast_io(B)
 
#define CUANTAS 15

main.c

#include "main.h"
 
int inicio = -1;
int32 segundos = 0;
int interrupciones = CUANTAS;
int32 repeticiones[] = {7 * 24 * 3600, 6 * 24 * 3600, 5 * 24 * 3600, 4 * 24 * 3600, 3 * 24 * 3600, 2 * 24 * 3600, 24 * 3600, 12 * 3600};
int16 duraciones[] = {16000, 14000, 12000, 10000, 8000, 6000, 4000, 2000};
 
// Cuenta los segundos que van pasando para la activación automática del
// riego
#INT_RTCC
void cronometro()
{
    if(--interrupciones == 0) 
    {
      ++segundos;
      interrupciones=CUANTAS;
    }
}
 
// La interrupción es lanzada cuando el nivel del agua detectado por
// el sensor está por debajo de este y activamos el led de alarma
#INT_RB
void nivel()
{
   delay_ms(20);
   if(input(PIN_B4) == 1)
   {
      output_high(PIN_A0);
      output_low(PIN_A3);
   }
   else
   {
      output_low(PIN_A0);
   }
}
 
// La interrupción es lanzada cuando se pulsa el botón
#INT_EXT
void empiece()
{
   delay_ms(20);
   if(inicio == 0)
   {
      inicio = 1;
   }
}
 
void main()
{
   int contador;
 
   set_timer0(0);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   port_b_pullups(TRUE);
   set_tris_a(0);
   output_low(PIN_A0);
   output_low(PIN_A1);
   output_low(PIN_A3);
   enable_interrupts(INT_RTCC);
   enable_interrupts(INT_RB);
   enable_interrupts(INT_EXT);
   enable_interrupts(GLOBAL);
   delay_ms(1000);
   inicio = 0;
 
   for(;;)
   {
      if(inicio == 1)
      {
         // No dejamos que se pulse de nuevo el botón mientras dure el proceso
         disable_interrupts(INT_EXT);
         // Parpadeamos durante 4 segundos el led de trabajo
         for(contador = 0; contador < 8; contador++)
         {
            if(contador % 2 == 0)
            {
               output_high(PIN_A1);
            }
            else
            {
               output_low(PIN_A1);
            }
            delay_ms(1000);
         }
 
         // Si el nivel del agua está por encima del sensor activamos la bomba
         if(input(PIN_B4) == 0)
         {
            output_high(PIN_A1);
            output_high(PIN_A3);
            delay_ms(duraciones[input_b() >> 5]);
         }
         output_low(PIN_A1);
         output_low(PIN_A3);
         enable_interrupts(INT_EXT);
         delay_ms(1000);
         interrupciones = CUANTAS;
         segundos = 0;
         inicio = 0;
 
      }
      // Si el contador de segundos ha llegado a 0 es que hay que empezar 
      // el proceso de riego
      else if(segundos >= repeticiones[(input_b() & 0x0F) >> 1])
      {
         inicio = 1;
      }
   }
}

En el array repeticiones se ponen los milisegundos que dura el intervalo entre riegos, para poder seleccionarlo luego con los jumpers. En el array duraciones se ponen los milisegundos que dura el riego, para poder seleccionarlo luego con los jumpers.

Nada más arrancar el sistema empieza a contar los segundos que quedan para el siguiente riego, según esté configurado en los jumpers. Cuando llega ese momento o se pulsa el botón, el led de trabajo parpadea 4 segundos, permaneciendo encendido al igual que el relé para que funcione la bomba de agua mientras dure el tiempo de riego, según este configurado en los jumpers. Después el led se apaga, se desactiva el relé (y por consiguiente la bomba) y se espera de nuevo al evento de activación (por tiempo o hasta que se pulse de nuevo el botón).

Si el nivel del agua cae por debajo del sensor mientras se está regando se para automáticamente el riego desactivando el relé y encendiendo el led de alarma, que no se apagará hasta que el nivel esté otra vez por encima del sensor. Las siguientes veces que salte el evento por tiempo o por el botón, no activará el relé, protegiendo así a la bomba de estropearse.

Finalmente un video de cómo funciona el conjunto:

Interactuar con la videocámara MD80 y un microcontrolador

Electrónica, Proyectos 37 Comentarios

Hace poco escribí una reseña sobre el clon de la videocámara MINIDV MD80. Al ser una videocámara de tamaño reducido se puede usar en los proyectos de electrónica que queramos, sin embargo su autonomía viene marcada por la batería que contiene, además de que el proceso de encendido/apagado y grabación/parada es manual. Mi intención ha sido sustituir la batería por otra fuente de alimentación (una batería más grande, un transformador de la corriente eléctrica, etc) y que tanto el encendido como la grabación se hagan a través de un microcontrolador. En este artículo voy a explicar los pasos que he seguido para conseguir mi objetivo.

En principio he desmontado la carcasa de la videocámara y he extraido la electrónica y la batería:

He desoldado la batería y en su lugar he puesto dos cables para unirlos a una protoboard (aunque podrían soldarse a una placa de topos o a una pcb). Después he soldado dos cables de wrapping a los terminales traseros del botón de encendido y otro cable de wrapping al terminal trasero izquierdo del botón de grabación:

A partir de aquí ya se puede usar la cámara con un microcontrolador. Para este artículo me he decantado por un pic 12f683. La idea es usar dos transistores NPN para que hagan la simulación de pulsar tanto el botón de encendido como el de la grabación, para este artículo he usado 2 BC547. El microcontrolador activará la base del transistor a través de una resistencia de 390K. y comunicará el colector con el emisor como si de una pulsación manual del botón se tratase. Sólo hay que tener en cuenta que para el botón de encendido, el cable de wrapping de la derecha tiene que ir al colector y el cable de la izquierda al emisor, y en el botón de grabación el único cable va al colector y el emisor va a masa (GND).

La batería que trae la videocámara es una LiPo de una sola celda y tiene una capacidad de 230 mAh. Esto nos da la pista de que a la cámara debemos alimentarla con una tensión de 3,7 v. Para ello he usado un regulador Step-Down basado en el chip AX3022 de sure electronics. Este regulador ofrece hasta 1,5 A., más que suficiente para alimentar la videocámara y el microcontrolador (que puede funcionar a 3,7 v.).

Finalmente así queda el conjunto en una protoboard:

El ejemplo que he programado en el microcontrolador es muy sencillo: Espera 5 segundos, activa el transistor del botón de encendido durante medio segundo, esto hace que la videocámara se encienda, espera otros 5 segundos, activa el transistor del botón de grabación durante medio segundo, esto hace que la videocámara empiece a grabar, espera 10 segundos, activa el transistor del botón de grabación durante medio segundo, esto hace que la grabación pare, espera 5 segundos y finalmente activa el transistor del botón de encendido durante medio segundo, que provoca que la videocámara se apague:

El código fuente del programa del pic:

#include "main.h"
 
void power()
{
   output_high(PIN_A0);
   delay_ms(500);
   output_low(PIN_A0);
}
 
void record()
{
   output_high(PIN_A1);
   delay_ms(500);
   output_low(PIN_A1);
}
 
void main()
{
 
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC);
   setup_vref(FALSE);
   setup_oscillator(OSC_8MHZ);
 
   // TODO: USER CODE!!
   set_tris_a(0);
   output_a(0);
   delay_ms(5000);
   power();
   delay_ms(5000);
   record();
   delay_ms(10000);
   record();
   delay_ms(5000);
   power();
 
   for(;;);
}

Cuando vayamos a conectar la videocámara a un puerto USB para ver las grabaciones, no debemos olvidarnos de desenchufar los cables de alimentación que hemos soldado, ya que la fuente de alimentación ya no es la batería original que contenía.

Listar los grupos de un usuario de Active Directory en C#

Proyectos Sin comentarios

Queremos hacer una aplicación para windows (o para la Web) en .NET donde debemos comprobar si un usuario pertenece o no a unos grupos determinados del dominio.  Esto nos sirve para saber si el usuario puede entrar en la aplicación y qué permisos tiene. Lo normal es que un grupo contenga a un usuario directamente, pero, ¿que sucede si un usuario no pertenece directamente a un grupo A, sino que pertenece a un grupo B que a su vez pertenece al grupo A?.

Para que funcione en todos los casos se debe buscar el aributo tokenGroups de la cuenta del usuario en el Directorio Activo. Este atributo contiene todos los SIDs de los grupos a los que pertenece el usuario directa o indirectamente.

Aquí pongo un ejemplo comentando de cómo se puede hacer:

// Creamos un objeto DirectoryEntry para conectarnos al directorio activo
DirectoryEntry adsRoot = new DirectoryEntry("LDAP://" + Environment.GetEnvironmentVariable("USERDOMAIN"));
// Creamos un objeto DirectorySearcher para hacer una búsqueda en el directorio activo
DirectorySearcher adsSearch = new DirectorySearcher(adsRoot);
 
try
{
    // Ponemos como filtro que busque el usuario actual
    adsSearch.Filter = "samAccountName=" + Environment.GetEnvironmentVariable("USERNAME");
 
    // Extraemos la primera coincidencia
    SearchResult oResult;
    oResult = adsSearch.FindOne();
 
    // Obtenemos el objeto de ese usuario
    DirectoryEntry usuario = oResult.GetDirectoryEntry();
 
    // Obtenemos la lista de SID de los grupos a los que pertenece
    usuario.RefreshCache(new string[] { "tokenGroups" });
 
    // Creamos una variable StringBuilder donde ir añadiendo los SID para crear un filtro de búsqueda
    StringBuilder sids = new StringBuilder();
    sids.Append("(|");
    foreach (byte[] sid in usuario.Properties["tokenGroups"])
    {
        sids.Append("(objectSid=");
        for (int indice = 0; indice < sid.Length; indice++)
        {
            sids.AppendFormat("\\{0}", sid[indice].ToString("X2"));
        }
        sids.AppendFormat(")");
    }
    sids.Append(")");
 
    // Creamos un objeto DirectorySearcher con el filtro antes generado y buscamos todas la coincidencias
    DirectorySearcher ds = new DirectorySearcher(adsRoot, sids.ToString());
    SearchResultCollection src = ds.FindAll();
 
    // Recorremos toda la lista de grupos devueltos
    foreach (SearchResult sr in src)
    {
        String sGrupo = (String) sr.Properties["samAccountName"][0];
        // A partir de aquí hacer lo que corresponda con cada grupo
        ...
    }
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}

Manifiesto por una Red Neutral

Proyectos Sin comentarios

Los ciudadanos y las empresas usuarias de Internet adheridas a este texto manifestamos:

  1. Que Internet es una Red Neutral por diseño, desde su creación hasta su actual implementación, en la que la información fluye de manera libre, sin discriminación alguna en función de origen, destino, protocolo o contenido.
  2. Que las empresas, emprendedores y usuarios de Internet han podido crear servicios y productos en esa Red Neutral sin necesidad de autorizaciones ni acuerdos previos, dando lugar a una barrera de entrada prácticamente inexistente que ha permitido la explosión creativa, de innovación y de servicios que define el estado de la red actual.
  3. Que todos los usuarios, emprendedores y empresas de Internet han podido definir y ofrecer sus servicios en condiciones de igualdad llevando el concepto de la libre competencia hasta extremos nunca antes conocidos.
  4. Que Internet es el vehículo de libre expresión, libre información y desarrollo social más importante con el que cuentan ciudadanos y empresas. Su naturaleza no debe ser puesta en riesgo bajo ningún concepto.
  5. Que para posibilitar esa Red Neutral las operadoras deben transportar paquetes de datos de manera neutral sin erigirse en “aduaneros” del tráfico y sin favorecer o perjudicar a unos contenidos por encima de otros.
  6. Que la gestión del tráfico en situaciones puntuales y excepcionales de saturación de las redes debe acometerse de forma transparente, de acuerdo a criterios homogéneos de interés público y no discriminatorios ni comerciales.
  7. Que dicha restricción excepcional del tráfico por parte de las operadoras no puede convertirse en una alternativa sostenida a la inversión en redes.
  8. Que dicha Red Neutral se ve amenazada por operadoras interesadas en llegar a acuerdos comerciales por los que se privilegie o degrade el contenido según su relación comercial con la operadora.
  9. Que algunos operadores del mercado quieren “redefinir” la Red Neutral para manejarla de acuerdo con sus intereses, y esa pretensión debe ser evitada; la definición de las reglas fundamentales del funcionamiento de Internet debe basarse en el interés de quienes la usan, no de quienes la proveen.
  10. Que la respuesta ante esta amenaza para la red no puede ser la inacción: no hacer nada equivale a permitir que intereses privados puedan de facto llevar a cabo prácticas que afectan a las libertades fundamentales de los ciudadanos y la capacidad de las empresas para competir en igualdad de condiciones.
  11. Que es preciso y urgente instar al Gobierno a proteger de manera clara e inequívoca la Red Neutral, con el fin de proteger el valor de Internet de cara al desarrollo de una economía más productiva, moderna, eficiente y libre de injerencias e intromisiones indebidas. Para ello es preciso que cualquier moción que se apruebe vincule de manera indisoluble la definición de Red Neutral en el contenido de la futura ley que se promueve, y no condicione su aplicación a cuestiones que poco tienen que ver con ésta.

La Red Neutral es un concepto claro y definido en el ámbito académico, donde no suscita debate: los ciudadanos y las empresas tienen derecho a que el tráfico de datos recibido o generado no sea manipulado, tergiversado, impedido, desviado, priorizado o retrasado en función del tipo de contenido, del protocolo o aplicación utilizado, del origen o destino de la comunicación ni de cualquier otra consideración ajena a la de su propia voluntad. Ese tráfico se tratará como una comunicación privada y exclusivamente bajo mandato judicial podrá ser espiado, trazado, archivado o analizado en su contenido, como correspondencia privada que es en realidad.

Europa, y España en particular, se encuentran en medio de una crisis económica tan importante que obligará al cambio radical de su modelo productivo, y a un mejor aprovechamiento de la creatividad de sus ciudadanos. La Red Neutral es crucial a la hora de preservar un ecosistema que favorezca la competencia e innovación para la creación de los innumerables productos y servicios que quedan por inventar y descubrir. La capacidad de trabajar en red, de manera colaborativa, y en mercados conectados, afectará a todos los sectores y todas las empresas de nuestro país, lo que convierte a Internet en un factor clave actual y futuro en nuestro desarrollo económico y social, determinando en gran medida el nivel de competitividad del país. De ahí nuestra profunda preocupación por la preservación de la Red Neutral. Por eso instamos con urgencia al Gobierno español a ser proactivo en el contexto europeo y a legislar de manera clara e inequívoca en ese sentido.

(Si te sientes cómodo y representado por este texto, dale toda la difusión que puedas y quieras: reprodúcelo, enlázalo, tradúcelo, compártelo, vótalo… todas esas cosas que puedes hacer con total tranquilidad y libertad gracias, precisamente, al hecho de que tenemos todavía una red neutral. Hagamos posible el seguir teniéndola)

Clon de videocámara MD80

Reseñas 6 Comentarios

Hace algún tiempo me compré un clon de la videocámara MD80. Esta cámara, que se puede comprar por unos 11 € ( http://www.dealextreme.com/details.dx/sku.32022 ) tiene una relación calidad/precio inmejorable. Graba imagenes a 720×480 pixeles / 30 fps en formato AVI con el codec Motion JPEG y sonido PCM en mono a 22050 hz. Los videos se almacenan en una tarjeta microSD (recomendable class 4 o incluso class 6) de hasta 16 GB. La batería es recargable por el puerto USB y dura para una grabación de 80 minutos. Puede grabar automáticamente cuando detecta un sonido por encima de los 60 db. Además puede funcionar como webcam. Trae consigo varios soportes para que se puedan colocar el distintos sitios. Es sensible a la luz y se ajusta automáticamente.

La verdad es que es muy completa y la gente la suele usar mucho cuando quiere salir al campo en bicicleta y grabar un vídeo. Su tamaño tan reducido y su peso de 20 gramos hace que sea ideal para colocarla en cualquier sitio.

No todo son maravillas, por ejemplo la cámara no tiene estabilizador, por lo que se notarán los movimientos abruptos. También la grabación no es del todo fluida, pero eso ya depende de las condiciones de luz. Importante lo de la clase de la memoria microSD, ya que si no es lo suficientemente rápida veremos saltos en los videos. Por otro lado muestra la fecha y la hora en la grabación, pero esto tiene arreglo.

Finalmente dejo un video que he grabado por el centro de Madrid y en el parking de un conocido centro comercial:

Convertir un fichero wav comprimido con el codec G729A

Informática, Proyectos 2 Comentarios

Recientemente me ha surgido la necesidad de convertir unos ficheros de audio que estaban en formato .WAV comprimido con el codec G729A a un formato .WAV reproducible en cualquier PC para hacer unas pruebas. Se trataba de poder escuchar ficheros .WAV que estaban comprimidos con ese codec, pero dada la dificultad de encontrar el codec gratuitamente para poder instalarlo en Windows he encontrado una alternativa que sirve igualmente.

En la página de Voice Age he encontrado una librería gratuita para windows con ejemplos y ejecutables de cómo extraer el audio comprimido con un codec G729A. Si se va a usar comercialmente se debe pedir una licencia a sipro que es quien tiene los derechos de licenciamiento.

El problema con los ejecutables de Voice Age es que convierte de formato G729A a formato RAW, pero ni el origen tiene los datos por sí sólo (están embebidos en un fichero .wav) ni en la conversión se pasa a un fichero .wav reproducible en windows.

Por ello he creado un programa de consola en Visual C++ 6.0 que hace uso de la librería .lib que ofrece VoiceAge para hacer esos dos pasos. La idea es leer unos datos del fichero wav y moverse hasta el byte 44 del fichero wav de origen, accediendo así a los datos de audio directamente; crear un fichero .wav de destino,  introduciendo en este la cabecera de un fichero .wav con el tamaño de los datos de audio descomprimido e indicando que tiene formato PCM; finalmente leer los datos de audio comprimido, descomprimirlos con la librería y escribirlos en el fichero de destino.

Este es el código fuente del mismo:

#include "stdafx.h"
#include "va_g729.h"
#include <stdio.h>
#include <memory.h>
 
int main(int argc, char* argv[])
{
	FILE *entrada;
	FILE *salida;
	unsigned char	serial[L_FRAME_COMPRESSED];
	short			synth[L_FRAME];
	int				bfi;
	unsigned char cabecera[] = {0x52,0x49,0x46,0x46,0x00,0x00,0x00,0x00,0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x40,0x1F,0x00,0x00,0x80,0x3E,0x00,0x00,0x02,0x00,0x10,0x00,0x64,0x61,0x74,0x61,0x00,0x00,0x00,0x00};
	unsigned long longitud;
	unsigned long *puntero;
 
	puts("SISTEMAS O.R.P.\nConvertidor de WAVs G729A a PCM\n");
 
	// Se comprueban los parámetros
	if(argc != 3)
	{
		puts("Debe introducir el archivo wav de origen y el archivo wav de destino.");
		return 1;
	}
 
	// Se abre el fichero de origen
	entrada = fopen(argv[1], "rb");
	if(entrada == NULL)
	{
		puts("No se pudo abrir el fichero de origen");
		return 2;
	}
 
	// Se abre el fichero de destino
	salida = fopen(argv[2], "wb");
	if(salida == NULL)
	{
		puts("No se pudo abrir el fichero de destino");
		fclose(entrada);
		return 2;
	}
 
	// Se comprueba que el fichero origen es un .wav
	fread(serial, sizeof(char), 4, entrada);
	if(memcmp(serial, "RIFF", 4) != 0)
	{
 
		puts("El fichero de entrada no es un wav");
		fclose(entrada);
		fclose(salida);
		return 3;
	}
 
	// Se lee el tamaño de los datos de audio del fichero origen
	// y se calcula el tamaño de los datis de audio del fichero destino
	fread(&longitud, sizeof(unsigned long), 1, entrada);
	puntero = (unsigned long *)(cabecera + 4);
	*puntero = 36 + (longitud - 36) * L_FRAME / L_FRAME_COMPRESSED * 2;
	puntero = (unsigned long *)(cabecera + 40);
	*puntero = (longitud - 36) * L_FRAME / L_FRAME_COMPRESSED * 2;
	// Se escribe la cabecera wav al fichero de destino
	fwrite(cabecera, sizeof(char), sizeof(cabecera), salida);
	// Saltamos al byte 44 donde se encuentran los datos de audio comprimido
	fseek(entrada, 44, SEEK_SET);
 
	// Se inicializa el descompresor
	va_g729a_init_decoder();
 
	// Se lee un grupo de bytes comprimidos y se escriben descomprimidos
	while (fread(serial, sizeof(char), L_FRAME_COMPRESSED, entrada) == L_FRAME_COMPRESSED)
	{
		bfi = 0;
		va_g729a_decoder(serial, synth, bfi);
		fwrite(synth, sizeof(short), L_FRAME, salida);
	}
 
	fclose(entrada);
	fclose(salida);
 
	puts("Proceso terminado");
 
	return 0;
}

Y aquí dejo el fichero ejecutable:

G729AtoPCM

El Bus Pirate

Electrónica 2 Comentarios

Bueno, pues me ha llegado el bus pirate y es una maravilla.

Se trata de una placa open hardware que sirve para medir y/o interactuar de una forma simple con un montón de cosas. Se conecta por puerto usb a un ordenador y con una interfaz serie podemos enviar comandos al bus pirate. Esto es lo que puede hacer:

  • Tomar medidas de 0 a 6 voltios.
  • Medir frecuencias de 1 Hz a 40 MHz
  • Generar PWM de 1 kHz a 4 MHz
  • Interactuar con dispositivos UART, SPI, I2C, MIDI, teclado, LCD, JTAG, etc
  • Analizador lógico de 10 Hz a 1 MHz
  • Se pueden hacer scripts desde Perl, Python, etc.
  • Se puede reprogramar para ser un programador de AVR o de PIC

Mi primera prueba ha sido medir la temperatura de un LM35:

Que indica que en la habitación tengo 32 grados (calorcito hace, si :) )

La verdad es que es muy cómodo y muy rápido para probar todos aquellos componenes que en otro caso tendríamos que desplegar un montón de componentes para obtener algún resultado.

Alarma con un pic 16f628, un transceptor ds275, sensor de movimiento y un modem antiguo

Electrónica, Proyectos 72 Comentarios

Tenía un modem externo antiguo de 2400 b.p.s. que me regalaron y se me ocurrió la idea de usarlo para crear una alarma. Los bueno de los modems es que puedes interactuar con la línea telefónica para hacer llamadas.

La idea consiste en que un sensor de movimiento piroeléctrico de infrarrojos (PIR) detecte el movimiento de una persona en una sala, este informaría de la detección a un pic y este sería el encargado de llamar a un número de teléfono a través del modem.

El matería que he usado es:

Para hacer el circuito lo más sencillo posible he usado un pic 16f628a dado que con él podemos ahorrarnos el poner un cristal de cuarzo externo, poder desactivar el pin MCLR y usar la USART que trae consigo (podría haber usado un pic 16f88 igualmente). También he usado el transceptor ds275 porque se puede comunicar con un puerto serie como el del modem sin añadirle ningún condensador como ocurre con el MAX232. Como no quería depender de una fuente de alimentación externa, he soldado unos cables al modem para obtener de este los 5 voltios necesarios para el pic y el transceptor y para la alimentación de 9 voltios del sensor PIR.

Este es el esquema del circuito:

El sensor PIR tiene una salida digital que va conectada al pin RB0 del pic, que se puede programar como interrupción, por lo que el pic puede permanecer en modo sleep hasta que recibe la interrupción del sensor y empezar a mandar los comandos AT para hacer la llamada de alarma. Como el PIC no puede por sí sólo interactuar con un puerto serie, el transceptor convierte las señales del PIC a señales RS232 para enviárselas al puerto serie del modem y viceversa. Una vez que se ha hecho la llamada y el modem detecta que se ha colgado la linea, devolverá un código numérico que el pic recibirá para volver a dejar el sistema en modo de detección.

Foto del conjunto:

Un video de demostración:

Lo que sucede en el video es lo siguiente: Se enciende el modem y el pic configura este para que no tenga eco los comandos, para que devuelva respuestas numéricas y que el altavoz esté desactivado, después de 15 segundos de espera para que el usuario pueda abandonar la habitación el pic se pone a esperar la detección de movimiento, cuando la detecta llama a un número de móvil prefijado para que el usuario reciba la alerta.

El código fuente del pic esta programado en CCS y es el siguiente:

main.h

#include <16F628A.h>
 
#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC                    //Internal RC Osc
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
 
#use delay(clock=4000000)
#use fast_io(b)
#use rs232(baud=9600,parity=N,xmit=PIN_B2,rcv=PIN_B1,bits=8)
 
typedef enum {INICIO, DETECTA, ALARMA, LLAMADO} ESTADOS;

main.c

#include "main.h"
 
ESTADOS estado;
volatile int8 dato;
 
// se ha detectado un movimiento y se pasa al estado de alarma
#INT_EXT
void movimiento()
{
   if(estado == DETECTA)
   {
      estado = ALARMA;
   }
}
 
// se ha detectado un carácter en la cola la USART
#INT_RDA
void lectura()
{
   int8 valor;
   if(kbhit())
   {
      valor = getc();
      if(valor != '\r')
      {
         dato = valor;
      }
   }
}
 
// función genérica que pasa al estado de detección borrando cualquier interrupción anterior y activándola 
void deteccion()
{
   estado = DETECTA;
   clear_interrupt(int_ext);
   enable_interrupts(int_ext);
}
 
void main()
{
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   setup_oscillator(OSC_4MHZ);
   // Ponemos a todos los pines del puerto B como entrada excepto el de salida de la USART 
   set_tris_b(0xFB);
   // Empezamos por el estado de inicio
   estado = INICIO;
   // La interrupción debe ser cuando pasa de nivel bajo a nivel alto
   ext_int_edge( L_TO_H );
   enable_interrupts(int_rda);
   enable_interrupts(global);
 
   // TODO: USER CODE!!
 
   for(;;)
   {
      switch(estado)
      {
         // En el estado de inicio inicializamos el modem para que no haga eco de los comandos, 
         // los valores de respuesta sean numéricos y apague el altavoz del modem, esperando
         // 15 segundos para dejar salir al usuario de la sala y pasamos al estado de detección
         case INICIO:
            delay_ms(2000);
            printf("ATE0V0M0\r");
            delay_ms(13000);
            deteccion();
            break;
         // En el estado de detección esperamos la interrupción en modo sleep para consumir menos
         case DETECTA:
            sleep();
            break;
         // Se desactiva la interrupción y marcamos el número de teléfono que queramos alertar
         case ALARMA:
            disable_interrupts(int_ext);
            dato = 'A';
            printf("ATDTXXXXXXXXX\r");
            estado = LLAMADO;
            break;
         // Esperamos a que termine la llamada para que vuelva al estado de detección
         case LLAMADO:
            if(dato != 'A')
            {
               deteccion();
            }
      }
   }
 
}

Sólo comentar que XXXXXXXXX hay que sustituirlo por el número de teléfono al que queramos dar la alarma.

« Artículos anteriores Siguientes artículos »