Búsqueda personalizada

22 septiembre 2007

Control Webcam - Arduino & Linksys

Mas proyectos en: http://eduardomarin.es/node/2

Nota: Versión revisada con servidor thttp + php aquí.

Hacía ya casi un año que no volvía a trastear con la placa Arduino y con el router Linksys, desde que monté el display en este último.

Pero debe ser la presión psicológica que nos ofrece la vuelta a los libros la que haya hecho que me haya puesto a matar el tiempo entre circuitos y código.

Idea--

La idea de este proyecto es recrear los sistemas de control de cámaras de seguridad que pueden verse en muchos centros comerciales o locales. Desde luego, la estética que suelen tener estos dispositivos (una semiesfera de cristal ahumado en cuyo interior gira la cámara) dista mucho de la que yo he conseguido, pero la funcionalidad se aproxima bastante.

Además, dado que el control de Arduino se realiza a través del router Linksys WRT54G nos ofrece la posibilidad de operar el sistema remotamente a través de un cliente web, que es lo que aquí mostraremos.

Así, resumiendo, tenemos una Webcam (que realiza streaming) y que está montada sobre un "brazo" compuesto por dos servomotores que son controlados por Arduino. Éste a su vez recibe a través del puerto serie que instalamos en el router Linksys las señales procedentes del cliente Web que envía la señal desde cualquier punto de Internet.



Montaje "Hardware"--

Aunque el movimiento conseguido no es el más óptimo, pues en ciertos ángulos se pierde la orientación espacial, se consiguen resultados satisfactorios mediante el montaje que hemos realizado. Hemos partido de dos servomotores para conseguir dos planos de rotación. El primer plano lo forma el servomotor que va acoplado directamente a la Webcam en su centro de giro, así, tendremos una panorámica de 360º sobre la horizontal. El otro servomotor va acoplado perpendicularmente sobre el anterior servo perimitiendo hacer giros de 360º sobre la vertical, barriendo así una esfera casi completa (por limitaciones de cableado).


Circuitería--

Señalamos que la parte principal se encuentra en torno a Arduino. Así, tendremos un cable "serie" que comunica a éste con el router (y que puede verse una explicación más detallada en este otro post), y la conexión que hemos tenido que realizar entre Arduino y los servomotores. El esquema puede verse en la siguiente imagen:



Código en Arduino--

Respecto al código que se implementa en Arduino hay que decir que parte se basa en esquemas y proyectos que ya realicé con anterioridad, sobretodo a la hora de comunicarse el router con la placa base. Aún así resumiré los conceptos fundamentales.

Por un lado, el router Linksys dispone de un corazón basado en linux y, por lo tanto, ofrece un shell y un intérprete de shell script que hace muy flexible y potente su uso. Además gracias al puerto serie que instalamos podemos enviar a Arduino flujos de bytes con significado subjetivo. En nuestro caso, y he aquí el cambio respecto a post anteriores, tan sólo tenemos que mover dos pines (que se asocian a los dos servomotores). Por otro lado, tengo que señalar que los servomotores han perdido la funcionalidad de posicionamiento, es decir, tuve que usarlos para rotación contínua y no he querido molestarme en volver a abrirlos para recuperarlos (aunque eso hubiese sido lo mejor). Debido a esto sus ángulos de posición no pueden controlarse con exactitud y su movimiento ahora se basan en tiempo de rotación. Por lo tanto, Linksys debe mandar a Arduino información sobre el tiempo que estarán rotando. Por conveniencia usaremos la señal Xi0000 para referirnos al servomotor X que girará en la dirección i (izquierda) y lo hará durante un tiempo de 0000 milisegundos (o microsegundos?? ya no recuerdo). Por lo tanto, en el router habrá un programa en shell script que se encargará de enviar a través del puerto serie dichas codificaciones.

En Arduino existe el siguiente código para manejar tanto la señal proveniente del puerto serie como la necesaria para manejar los servomotores:



#include "WProgram.h"

void pulseon(int rin, int pulso);

/* Adquisici\u00f3n de datos y control de salidas para

linksys WRT45GL a trav\u00e9s de puerto serie */



//Definimos las variables necesarias

int i,valor,valoraux,digito,pin,retraso;

byte dato,decenas,unidades,sentido,millares,centenas,motor;

int x = 13;

int y = 12;



void setup(){

for (i=2;i<=13;i++){

pinMode(i,OUTPUT);

}

Serial.begin(9600);

}

void pulseon (int rin, int pulso){

digitalWrite(rin,HIGH);

delayMicroseconds(pulso);

digitalWrite(rin,LOW);

}

void loop(){



//Comprobamos si llega se\u00f1al del router:

while(! Serial.available()){

}

motor=Serial.read();

if (motor=='X' || motor=='Y'){

while(! Serial.available()){

}

sentido=Serial.read();



while(! Serial.available()){

}

millares=Serial.read();

//eco de comprobacion serialWrite(decenas);

pin=(millares-48)*1000;



while(! Serial.available()){

}

centenas=Serial.read();

//eco de comprobacion serialWrite(decenas);

pin=pin + (centenas-48)*100;



while(! Serial.available()){

}

decenas=Serial.read();

//eco de comprobacion serialWrite(decenas);

pin=pin + (decenas-48)*10;



while(! Serial.available()){

}

unidades=Serial.read();

//eco de comprobacion serialWrite(unidades);

retraso=pin+(unidades-48);

if(motor=='X'){

if(sentido=='d'){

for (int i=0; i<= (retraso); i++){

pulseon(x,1700);

delay(19);

}

}

else{

for (int i=0; i<= (retraso); i++){

pulseon(x,1300);

delay(19);

}

}

}

if(motor=='Y'){

if(sentido=='d'){

for (int i=0; i<= (retraso); i++){

pulseon(y,1200);

delay(19);

}

}

else{

for (int i=0; i<= (retraso); i++){

pulseon(y,1513);

delay(19);

}

}

}

}



}






Una vez que los servomotores responden convenientemente a las señales que envía el router ya podemos trabajar algo más el interfaz con Linksys para hacerlo más intuitivo y atractivo.

Una de las ventajas de nuestro router es la de ofrecernos un servidor web con una puerta trasera a linux. Así, existe una comunicación entre nuestra web y nuestro linux totalmente transparente.

La idea del interfaz es visualizar las imágenes de la webcam y aportar los controles básicos para mover la cámara. Intuitivamente aparecen cuatro movimientos (arriba, abajo, derecha, izquierda) que implementaremos con cuatro simples botones. Como nuestro objetivo no es crear una página perfecta, usaremos html básico con un poquito de javascript que nos ofrece el programa que nos realiza el streaming de vídeo. Cada vez que oprimamos uno de los cuatro botones nos refrescará la página (he aquí una solución poco elegante) y ejecutará el código shellscript que mueve los servomotores.

Así, el código de nuestra página sería el siguiente:

<HTML><HEAD><BODY>

<script LANGUAGE="JavaScript">

<!--

function Mostrar(boton)

{

sc.executeScript("echo Xi0001>/dev/cua/1");

return true;

}

//-->

</script>



<div align= center><h1>Control Cámara Web</h1></div><br></br><br></br>



<div align=center>

<table>

<tr>

<td colspan="3"><div align=center>

<form method=get action="/cgi-bin/arriba.sh">

<input type="hidden" name="IsisScript" value="./prueba.sh">

<input type="submit" name="Arriba" value=" Arriba ">

</form>

</td>

</tr>

<tr>

<td><form method=get action="/cgi-bin/izquierda.sh">

<input type="hidden" name="IsisScript" value="./prueba.sh">

<input type="submit" name="Izquierda" value="<--">

</form>

</td>

<td><div align= center><img src="loading.jpg" class="webcam_in_frame" id="webcam1" onmousedown="PTZMouseDown1(event)" width="320" height="240" alt="webcam" /></div></td>

<td><form method=get action="/cgi-bin/derecha.sh">

<input type="hidden" name="IsisScript" value="./prueba.sh">

<input type="submit" name="Derecha" value="-->">

</form>

</td>

</tr>

<tr>

<td colspan="3"><div align=center><form method=get action="/cgi-bin/abajo.sh">

<input type="hidden" name="IsisScript" value="./prueba.sh">

<input type="submit" name="Abajo" value=" Abajo ">

</form>

</div></td>

</tr>



</table></div>





<script type="text/javascript">

<!--

errorimg1= 0;

document.images.webcam1.onload = DoIt1;

document.images.webcam1.onerror = ErrorImage1;

function LoadImage1()

{

uniq1 = Math.random();

document.images.webcam1.src = "http://xxxxx/cam_1.jpg?uniq="+uniq1;

window.status = "[powered by webcamXP]";

}

function PTZMouseDown1(e)

{

var IE = document.all?true:false;

var x,y;

var myx,myy;

var myifr = document.getElementById("_iframe-ptz");

tp = getElPos1();

myx = tp[0];

myy = tp[1];

if(IE){

x = event.clientX - myx + document.body.scrollLeft;

y = event.clientY - myy + document.body.scrollTop;

} else {

x = e.pageX - myx;

y = e.pageY - myy;

}

//alert(x + " :: " + y);

if (myifr != null) myifr.src = "http://xxxxx/ptz?source=1&moveto_x=" + x + "&moveto_y=" + y +"";

return true;

}

function getElPos1()

{

el = document.images.webcam1;

x = el.offsetLeft;

y = el.offsetTop;

elp = el.offsetParent;

while(elp!=null)

{ x+=elp.offsetLeft;

y+=elp.offsetTop;

elp=elp.offsetParent;

}

return new Array(x,y);

}

function ErrorImage1()

{

errorimg1++;

if (errorimg1>1){

document.images.webcam1.onload = "";

document.images.webcam1.src = "http://www.darkboard.net/webcam/offline.jpg";

}else{

uniq1 = Math.random();

document.images.webcam1.src = "http://xxxxx/cam_1.jpg?uniq="+uniq1;

}

}

function DoIt1()

{

errorimg1=0;

window.setTimeout("LoadImage1();", 70);

}

//-->

</script>

</BODY>

</HTML>



Y el código de uno de los botones (arriba) sería este otro:


#!/bin/sh

echo Xd0001>/dev/cua/1

echo Content-type: text/html

echo ""



/bin/cat << EOM

<HTML>

<HEAD>

<META HTTP-EQUIV="Refresh" CONTENT="0; URL=/Mis_Webs/webcam/webcam.html">

</HEAD>



EOM




Podéis ver fotos:





Y vídeo:


10 comentarios:

WoLo dijo...

¡Que maravilla!

Te voy a encargar uno de estos para cuando me vaya a Gales tener la casa controlada.

javi dijo...

Podrías detallar un poco más el control de los motores ?

José Pablo dijo...

Hola,

Me ha encantado tu idea, pero tengo una pregunta. ¿Se podría comunicar el router con la placa arduino a traves de una conexión ethernet?. Dado que existen placas arduino con ethernet.

Si es posible a traves de ethernet con poco trabajo puede poner una camara por cada puerto ethernet del route ( hasta 4 camaras).

Sin más Gracias y me parece muy original y util.

Shawe dijo...

Puedes mejorar tu control via web usando AJAX, ya que ahora te recarga TODA la pagina, aunque apenas tenga nada, mientras que SOLO necesitas actualizar la capa de la imagen.

Puede que te parezca una tontería, pero te resultará mucho más efectivo.

Shawe dijo...

Por cierto, si el proyecto lo haces a nivel personal, sería bueno que lo liberarás bajo alguna licencia libre junto a toda la documentación posible para reproducirlo y mejorarlo, puede ser útil para muchísimos fines y no creo que su coste sea demasiado elevado.

kayak dijo...

Buenas Shawe,

sin duda retocar el cliente web mediante Ajax mejoraría mucho su apariencia.

El problema es que últimamente ando muy liado y no tengo tiempo ni pa respirar, y el caso es que tengo algún proyectillo pendiente todavía con arduino.

Espero poder ponerme pronto con ello, y tu idea sí me parece muy buena (compartir el saber).

Un saludo Shawe.

Anónimo dijo...

Mira soy Fernando de Argentina y no uso Arduino solo armé tu página HTML y puse tu script arriba.sh en el router como para que me tire por el port serie del router la palabra "arriba". Si lo ejecuto manualmente como ( ./arriba.sh ) funciona correctamente pero cuando lo quiero ejecutar desde el HTML no funciona. En que me puedo estar equivocando ?. No me interesa la camara web sólo quiero poder mandar caracteres por dicho port. Cualquier ayuda que puedan darme se los agradezco. Mi mail es farmada@yahoo.com.

Gracias

Anónimo dijo...

Soy Fernando nuevamente. Mi emal seria f_armada@yahoo.com

Gracias

kayak dijo...

Versión revisada en http://ahorcandoeltiempo.blogspot.com/2008/11/control-webcam-20.html

Jose dijo...

Saludos,

Esta super interesante pero me gustaria saber si tienes algun tutorial para controlar una camara ip, con php o html.

Gracias