Detector de movimiento sobre un ATMega88

9 de marzo de 2010 por cogollo

Como mi último post se alargó más de la cuenta, dejé pendiente la implementación del sensor de presencia mediante nuestra nueva plataforma de desarrollo. Bueno, pues ya va tocando.

Por si ya lo has olvidado, vamos a montar un sistema que detecta la presencia de gente mediante un PIR. Si hay alguien presente enciende unas luces paulatinamente hasta llegar a su máximo de intensidad y después, tras un cierto tiempo sin que se detecte a nadie, las apaga también paulatinamente. Aquí tienes el vídeo otra vez:

Para completar este magnífico artefacto necesitarás montar unos pocos “periféricos” además de la placa con el micro. Veámoslos uno por uno después del salto.

Selector de tiempo

  • Un potenciómetro (entre 10KΩ y 1MΩ todo vale).
  • Cable plano de 3 conductores (de nuevo, los viejos cables IDE vienen muy bien para esto).
  • Una tira de pines macho.

Lo que queremos hacer con esto es conectar las dos patas exteriores del potenciómetro a 0 y 5 V respectivamente. De esta manera el voltaje que obtengamos en la pata central (que está conectada electricamente al cursor del potenciómetro) será proporcional al ángulo que tenga e l cursor en es momento. Esto es, si giramos el cursor todo a la izquierda tendremos 0V en la pata central. Si lo giramos todo a la derecha tendremos 5V. Y en las posiciones intermedias tendremos un valor proporcional al de su posición.

Luego, conectaremos este selector al puerto de sensores y nuestro software se encargará de leer periódicamente el valor del voltaje para determinar cuanto tiempo tiene que esperar sin detectar a nadie antes de ponerse a apagar las luces. De este modo podremos ajustar dicho tiempo en directo como quien cambia el volumen de la tele.

El esquema del circuito es este:

Y este es el aspecto del sensor una vez terminado. Nada espectacular.

Potenciometro con conector

Fijate que he quitado el pin que va entre GND y 5V para que el hueco coincida con el agujero que hay “tupido” en el puerto de sensores. En este caso usé pines acodados, pero he descubierto que es más cómodo si usas pines rectos normales.

Sensor de presencia

  • Un sensor PIR (Los hay de muchos tipos, nosotros estamos usando éstos).
  • Una resistencia de 100KΩ.
  • Cable plano de 3 conductores.
  • Tira de pines macho.

Para que nos entendamos, los sensores PIR son una especie de cámara de muy baja resolución que sólo puede ver el infrarrojo. Como las personas (y los seres vivos en general) están más calientes que su entorno “brillan” (en el infrarrojo) más que el fondo. Cuando un PIR detecta ese cambio en el brillo en alguna zona de su campo de visión asume que tiene a alguien delante.

El alcance y el ángulo de visión de un PIR varían mucho según el modelo. Lo normal es que el alcance esté entre los 2’5 y los 10 metros, y que el ángulo se encuentre entre los 30º y los 100º.

Los modelos que estoy usando tienen tres pines. Uno va a tierra (GND), otro a 5V para la alimentación (Vdd) y el tercero nos da la salida del sensor (OUT). El voltaje que midamos en este pin será 5 V cuando no hay nadie presente y 0V cuando se detecte a alguien (ojo, ya ves que la salida esta invertida).PIRPinoutPor último, aunque no lo pone en la hoja de datos de estos PIRs, parece que es necesario utilizar una resistencia pull-down de 100KΩ entre la salida y tierra para que la señal sea lo bastante limpia.

Dicho lo cual, aquí tienes el esquema del circuito y una fotico con el resultado:

Cable de comunicaciones

  • Cable plano de 10 hilos.
  • Dos conectores hembra de cable plano de 10 hilos

Este es el tipo de cable que necesitaremos, en general, para conectar nuestros periféricos a la “placa base”. Solo tienes que ponerle los conectores en los extremos. Normalmente la gente usa herramientas especiales para cerrar bien los conectores, pero yo lo he estado haciendo golpeándolos con un mazo de goma y sale bastante bien. Lo único es que tienes que ir poco a poco, por que si un lado del conector se encaja más rápidamente que el otro se pueden partir las pestañas que hacen que se mantenga todo unido. También he ocluido los agujeros que corresponderian a los pines que faltan en los puertos de entrada/salida para que no se pueda colocar el cable incorrectamente.

Cable conexion plano 9 pines

Distribuidor

  • Pines machos dobles.
  • Pines hembra acodados.

Pues sí, tener 4 pines de entrada/salida agrupados en un solo conector está muy bien cuando vas a necesitarlos todos a la vez, cuando quieres “hablar” en serie con un dispositivo o controlar un pequeño motor. Pero, ¿qué pasa si solo quieres encender una luz? O mejor: dos luces, separadas entre sí, y controladas cada una por un pin distito del microcontrolador. En estos casos, necesitamos una plaquita que nos permita separar las señales y dirigir cada una a su destino final.

Estas placas presenta en un lado un conector donde enchufaremos el extremo final del cable de comunicaciones y en los otros lados entre 1 y 4 conectores más pequeños hasta donde llevan las 4 señales de datos individuales y el voltaje de alimentación.

En este proyecto vamos a usar solamente una señal, pero aquí tienes tambien el esquema del distribuidor para dos señales que seguro que viene bien en el futuro.

Para que quede un poco más claro puedes ver las fotos de ambos circuitos completados. (Pero no hagas caso del jumper que se ve en el centro del distribuidor de dos señales, resultó que no era muy buena idea).

Conectores intermedios 1 y 2 pines - frenteConectores intermedios 1 y 2 pines - traseras

Amplificador

Transistor - frente

Queremos encender luces, y queremos que sean unas luces que marquen alguna diferencia entre “oscuro” y “luminoso”, pero los puertos de datos de nuestro controlador sólo pueden proporcionar unos 40 mA de corriente por cada pin. Está bien para encender un led, pero no te harán llegar muy lejos en la noche. Además, dan una tensión de salida de 5V y las bombillas que queremos usar son de 12V (como las de los coches).

Necesitamos una manera de amplificar las señales que manda el controlador hasta que sean lo bastante potentes como para encender esas bombillas. Para ello usamos un transistor MOSFET tipo n a modo de interruptor. Los MOSFETs tienen tres patas, drenador (drain), fuente (source) y puerta (gate). La corriente principal (la que encenderá nuestra bombilla) entra por el drenador y sale por la fuente. Cuando la puerta se encuentra conectada a tierra (0V) se dice que el transistor está en corte y no permite pasar corriente entre drenador y fuente. Cuando el voltaje de la puerta supera cierto voltaje umbral (positivo para los tipo n y negativo para los tipo p) el transistor está en saturación y permite que pase gran cantidad de corriente entre drenador y fuente.

Las características más importantes para este tipo de transistores son 3: el voltaje fuente-drenador máximo (VDSs), que es la diferencia de potencial máxima que podemos poner entre fuente y drenador sin que pete todo; la intensidad de drenador máxima (ID), que es la intensidad máxima que puede aceptar el drenador sin quemarse; y el voltaje umbral ( VGS(th)) que, simplificando, sería el voltaje que hay que aplicarle a la puerta para que el transistor comience a conducir.

Queremos encender dos bombillas de 12V y 6 Watios cada una. Eso supone que van a consumir un Amperio entre las 2. Además, queremos controlar el encendido con un pulso de 5 V. Total, que necesitaremos un MOSFET con: VDSs>12V, ID>1A y VGS(th)<5V.

En cualquier tienda de electrónica puedes encontrar cientos, si no miles, de transistores que cumplan estos requisitos por menos de un lerele, pero también puedes encontrarlos entre la chatarra electrónica que tengas por casa. Para esta placa yo he usado un STW13NK80Z que encontré en una fuente de alimentación rota. Con VDSs=800V y ID=12A es como matar moscas a cañonazos, sí, pero al menos el cañón nos salió gratis. Busca componentes que tengan este aspecto y estén atornillados a un disipador. Casi seguro que serán algún tipo de transistor. Solo tienes que googlear el numero de serie que llevan impreso para saber de qué se trata exactamente.

Ahora que ya tienes transistor vamos a montar el circuito. Tanto la señal, como la alimentación llegan por el conector macho y se “reenvían” al conector hembra. De este modo se pueden encadenar varios de estos amplificadores si fuera necesario.

El módulo terminado, con la bombilla al lado tiene este aspecto:

Amplificador- frentetransistor - trasera

Firmware

Contra todo pronóstico ésta era la última pieza que quedaba por montar. Lo siguiente es hacer un programa que se encargue de “leer” el PIR  y el potenciómetro y de controlar la luz. Puedes copiar el mío de aquí abajo Como ves es muy sencillito y está comentado hasta aburrir. Ya hablamos hace meses de cómo compilarlo y grabarlo en el microcontrolador.

/*
 * Firmware detector de presencia para ATMega88. Lee la salida de un sensor PIR y
 * de un potenciómetro en el puerto C (PC0 y PC3 respectivamente).
 * Enciende una luz conectada a PD5 cuando el PIR detecta movimiento (salida > 128)
*/

// Frecuencia de funcionamiento (para las rutinas de delay).
#define F_CPU 1000000
#define TMULT

#include
#include 

//Funciones para inicializar y leer desde las unidades conversoras analógico-digitales
void initialiceADC(void);
unsigned char readADC(unsigned char channel);

//Funciones para inicializar y emplear PWM
void initialicePWM(void);
void analogOut(char port,unsigned char value);

int main(void)
{
	//Aquí se guarda el estado actual del programa
	unsigned char state=0;
	//El valor guardado en 'out' es el que se usará como salida mediante PWM
	unsigned char out = 0;
	unsigned char ant;
	//Aquí llevaremos una cuenta del tiempo transcurrido desde la última detección
	int timer = 0;

	//Configuramos los pines del puerto D como salida
	DDRD=0xff;

	//Inicializamos PWM y ADC
	initialicePWM();
	initialiceADC();

	//Bucle principal.
	while(1)
	{
		switch(state)
		{
			//Estado 0: Apagado, esperando a detectar algo
			case 0:
				//Cuando se detecta a alguien pasamos a estado 1.
				// La salida para a 10 (muy poco brillo)
				if(readADC(0)>128)
				{
					out=10;
					state = 1;
				}
				else out=0;
				break;
			//Estado 1: Aumentando el brillo
			case 1:
				//Aumentamos el en cada iteración. Como la percepción del
				//brillo es logarítmica tenemos que aumentarlo exponencialmente
				//para que se perciba como un aumento lineal. En concreto
				//multiplicamos el brillo anterior por 1.125
				ant = out;
				out+= out>>3;
				//Si el brillo anterior es mayor que el actual -> overflow
				//Fijamos el brillo en 255 y pasamos al estado 2
				if(ant>out)
				{
					out = 255;
					state = 2;
				}
				break;
			//Estado 2: Brillo máximo hasta que pase un cierto tiempo desde la última detección
			case 2:
				//Si se detecta movimiento reseteamos el temporizador
				if(readADC(0)>128)
					timer =0;
				//si no lo aumentamos
				else
					timer++;

				//El tiempo que debe pasar depende de la posición del cursor de potenciómetro
				//será un valor entre 0 y 255 multiplicado por TMULT (para TMULT = 4 abarca entre
				// 0 y 90 segundos aprox).
				//Cuando timer supera ese valor lo ponemos a 0 y pasamos al estado 3.
				if(timer>(readADC(3)*TMULT))
				{
					timer = 0;
					state = 3;
				}
				break;
			//Estado 3: Disminuyendo el brillo
			case 3:
				//Reducimos el brillo igual que lo aumentamos antes. En lugar de
				//multiplicarlo por 1.125 ahora lo hacemos por 0.875
				if(readADC(0)>128)
					state = 1;
				else
				{
					ant = out;
					out-= out>>3;
					//Si ant=out es que out ya era tan pequeño que al redondear tras la
					//división se queda igual. Fijamos out a 0 y volvemos a la espera
					//en el estado 0
					if(ant<=out)
					{
						out = 0;
						state = 0;
					}
				}
				break;

		}
		//En cada iteración actualizamos el valor de la salida a 'out'
		analogOut(0,out);
		//y esperamos un poco antes del siguiente paso
		_delay_ms(100);
	}
}

void initialiceADC(void)
{
	DIDR0=0x3F; //Deshabilita la entrada digital a traves de los pines de ADC

	//REFS(0,1) pone Vcc como voltaje de referencia
	//y ADLAR(1) justifica el valor leido a la izquierda para que solo tengamos que leer ADCH
	ADMUX= _BV(REFS0)|_BV(ADLAR);
	//ADEN habilita el uso de ADC y ADPS(0,1,1) pone el preescale del temporizador ADC a 8
	ADCSRA = _BV(ADEN)|_BV(ADPS1)|_BV(ADPS0);

}

unsigned char readADC(unsigned char channel)
{
	ADMUX= (ADMUX & 0xF0) | channel;
	//Poniendo ADSC a 1 comienza la conversión
	ADCSRA|=_BV(ADSC);
	//Sabemos que ha teminado cuando ADSC vuelve a valer 0
	while(ADCSRA & _BV(ADSC));
	//El resultado está en ADCH
	return ADCH;
}

void initialicePWM()
{
	//Configuramos PD5 y PD6 como salidas
	DDRD |= _BV(PD5)| _BV(PD6);
  	//WGM0(0,1,1) selecciona el modo 'fast PWM'. COM0A(1,0) y COM2B(1,0)
  	//proporciona PWM no invertido para las salidas A y B
       TCCR0A = _BV(WGM01)|_BV(WGM00) | _BV(COM0A1)|_BV(COM0B1);
       //CS0(0,1,0)configura el preescaler de timer0 a 8
       TCCR0B = _BV(CS01);
}

void analogOut(char port, unsigned char value)
{
	//Si 'port' es 1 usamos la salida B. Si es 0 la A
	if(port & 1)
		OCR0B = value;
	else
		OCR0A = value;
}

Y dicho lo cual solo queda ensamblarlo todo. Si ha ido todo bien no deberías poder conectar nada incorrectamente, pero asegúrate antes de enchufarlo todo a la corriente por primera vez. Aquí tienes un par de fotos para que puedas fijarte en donde va conectada cada cosa.

Bueno, pues ya tienes tu propio detector de movimiento. Pero tendrás que quedarte muy muy quieto para ver cómo se apaga la luz.

Publicado en Hackeos e ideas, Nuestros hacks, Open Source | 1 Comentario »


Un comentario


  1. Alvaro Dice:

    …”Normalmente la gente usa herramientas especiales para cerrar bien los conectores, pero yo lo he estado haciendo golpeándolos con un mazo de goma y sale bastante bien” ¡¡Aupá los de la ribera del ebró!!
    (Mola el cacharro)