Controlar Arduino via Web con Raspberry Pi

Blog   /   14 Comentarios Standard Post

En la última entrada comenté como llevar a cabo la conexión entre el Raspberry Pi y Arduino. Una vez completada la conexión entre ambos dispositivos a través de USB, en esta entrada vamos a ver como podemos acceder a nuestro Arduino a través de una página web alojada en nuestro Raspberry Pi.

Para comenzar lo primero que tenemos que hacer es instalar un servidor web en nuestro Raspberry Pi. En mi caso he instalado lightttpd, para ellos simplemente ejecutamos el siguiente comando:

sudo apt-get -y install lighttpd

Una vez instalado el servidor web, este se iniciará y accediendo a la ip de nuestro Raspberry, obtendremos una web como la siguiente:

lightttpd

Ahora vamos a ver como podemos hacer que nuestro servidor web pueda ejecutar archivos python. Para ello tenemos que habilitar el soporte Cgi-bin en nuestro servidor web. Para ello, tenemos que editar el siguiente archivo: /etc/lighttpd/lighttpd.conf. Dentro del mismo añadimos las líneas resaltadas (El resto del archivo en tu caso puede ser diferente):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
server.modules = (
        "mod_access",
        "mod_alias",
        "mod_compress",
        "mod_cgi",        "mod_redirect",
#       "mod_rewrite",
)
 
server.document-root        = "/var/www"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = 8080
 
$HTTP["url"] =~ "/cgi-bin/" {      cgi.assign = ( ".py" = "/usr/bin/python" )} 
index-file.names            = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
 
compress.cache-dir          = "/var/cache/lighttpd/compress/"
compress.filetype           = ( "application/javascript", "text/css", "text/html", "text/plain" )
 
# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"

Básicamente lo que hacemos es añadir el modulo de cgi (línea 5) y luego indicamos en que carpeta están disponibles los scripts del sistema que se pueden ejecutar desde la red. En mi caso es una carpeta que se llama cgi-bin dentro de la carpeta donde van alojados los archivos html (por defecto /var/www/).

Llegados a este punto ya tienes todo configurado para empezar a controlar tu Arduino a través de internet. Solo tenemos que crearnos una página web y los correspondientes scripts en python.

En el Arduino he cargado un programa muy similar al que usé en el ejemplo anterior, con la diferencia que de cada vez que se escribe en un puerto, se devuelve una cadena codificada de unos y ceros con el estado resultante de los puertos después de haber cambiado el estado. Este es el enlace al archivo.

El script en python que controla todo el sistema es el siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import cgi
import serial
import time
 
arguments = cgi.FieldStorage()
ser=serial.Serial('/dev/ttyACM0',9600)
if 'status' in arguments:
	ser.write('sts')
	print(ser.readline())
else:
 
	ser.write(arguments['port'].value)
	ser.write(arguments['value'].value)
	ser.write('sts')
	print(ser.readline())

El script es bastante sencillo, simplemente con la librería CGI leemos los diferentes parámetros de la URL y los enviamos vía USB (puerto serie) al arduino. Si el comando es status, simplemente pedimos al arduino que nos informe sobre el estado actual de los puertos sin llevar a cabo ningún cambio.

Finalmente solo tenemos que crearnos una web donde mostrar toda la información y poder actuar sobre los puertos. En mi caso me he creado la siguiente web:

ArduinoWeb

El fuente de esta página se muestra a continuación:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 <!DOCTYPE html>
<html lang="es">
<head>
	<title>Arduino Port Control</title>
	<meta charset="utf-8" />
	<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" ></script>
	<style type="text/css">
		.unknowStatus{background-color:#6c6d6f; width:45px; height:45px;margin:1px; float:left;}
		.disabled {background-color:#932626; width:45px; height:45px;margin:1px; float:left;}
		.enabled {background-color:#36ab36; width:45px; height:45px; margin:1px; float:left;}
 
</style>
</head>
 
<body>
	<script type="text/javascript">
		function successResponse(data, textStatus, jqXHR)
		{
			$('#port13').attr("class", (data[0]=='0')?'disabled':'enabled');
                       $('#port12').attr("class", (data[1]=='0')?'disabled':'enabled');
                       $('#port11').attr("class", (data[2]=='0')?'disabled':'enabled');
                       $('#port10').attr("class", (data[3]=='0')?'disabled':'enabled');
                       $('#port09').attr("class", (data[4]=='0')?'disabled':'enabled');	
		}
 
		function errorResponse(data, textStatus, jqXHR)
		{
			$('#port13').attr("class", 'unknowStatus');
                        $('#port12').attr("class", 'unknowStatus');
                       $('#port11').attr("class", 'unknowStatus');
                       $('#port10').attr("class", 'unknowStatus');
                       $('#port09').attr("class", 'unknowStatus');	
		}
 
		$(document).ready(function() 
		{
			$('#update').click(function() 
			{
				$.ajax({
                	url:   'http://192.168.2.5:8080/cgi-bin/arduino.py?port='+$('#port').val()+'&value='+$('#value').val(),
                	type:  'get',
                    success:  successResponse,
                    error: errorResponse
 
                 });
			});
 
			$.ajax({
                	url:   'http://192.168.2.5:8080/cgi-bin/arduino.py?status=1',
                	type:  'get',
                    success: successResponse,
                    error: errorResponse
                 });
 
 
		});
	</script>
	<h1>Port Control</h1>
	<select id="port">
		<option value="09">Port 9</option>
  		<option value="10">Port 10</option>
  		<option value="11">Port 11</option>
  		<option value="12">Port 12</option>
  		<option value="13">Port 13</option>
  	</select>
  	<select id="value">
		<option value="1">On</option>
  		<option value="0">Off</option>
  	</select>
  	<button id="update">Update</button>
  	<h1>Port Status</h1>
  	<table border="1">
		<tr>
			<td>Port 13</td>
			<td>Port 12</td>
			<td>Port 11</td>
			<td>Port 10</td>
			<td>Port 09</td>
 
		</tr>
		<tr>
			<td><div id="port13" class="unknowStatus"></div></td>
			<td><div id="port12" class="unknowStatus"></div></td>
			<td><div id="port11" class="unknowStatus"></div></td>
			<td><div id="port10" class="unknowStatus"></div></td>
			<td><div id="port09" class="unknowStatus"></div></td>
		</tr>	
	</table>
</body>
</html>

El código de la página es bastante simple, simplemente por medio de jQuery, hacemos una llamada AJAX al script python alojado dentro de la carpeta cgi-bin indicándole los parámetros que queremos cambiar. Una vez obtenemos la respuesta simplemente actualizamos los colores de los diferentes divs gracias a jQuery utilizando la información proporcionada en la respuesta del script python. De esta forma tenemos una realimentación real del estado del puerto de nuestro Arduino una vez hemos cambiado los valores.

Antes de terminar la entrada simplemente comentar algunos problemas con los que me he encontrado:

1) Cuando ya tenía el script python hecho, al ejecutar el script desde la consola del raspberry funcionaba todo correctamente pero al intentar ejecutarlo desde la web, se ejecutaba pero no cambiaba el estado del puerto. Esto se debe porque el script ejecutado desde cgi-bin no tiene permiso de escritura sobre el dispositivo serie del arduino. La solución más rápida y chapucera fue simplemente darle permisos de escritura/lectura y ejecución a todo el mundo sobre el dispositivo serie por medio del siguiente comando:

sudo chmod 777 /dev/ttyACM0

2) Cuando ya podía escribir sobre el puerto serie desde la web, el otro problema que me apareció fue que cada vez que escribía en el puerto serie, se reiniciaba el arduino. Para solucionar esto siguiendo este hilo. Simplemente tuve que añadir un condensador (en mi caso lo que tenía a mano 47uF) entre el pin reset del arduino y GND. De esta forma se evita que se reinicie el arduino cada vez que se ejecuta el script CGI-BIN.

con esto ya tenemos a nuestro Arduino accesible desde internet por medio de una web de control alojada en nuestro Raspberry PI abriendo una nueva puerta al control de nuestros dispositivos domóticos o aplicaciones de control a través de internet.

14 Comentarios en esta entrada

  1. José Luis dice:

    Muy interesante tu entrada del blog. ¿Serías tan amable de corregir el enlace del código fuente de arduino? . Pone “Este es el enlace del archivo”, pero no veo el enlace por ningún lado.

    Un saludo….

  2. EPIFANIO dice:

    Hola buenas tardes. José Luis

    En primer lugar felicitarte por el artículo de control de arduino vía web . Esta muy bien descrito y para mucha gente como como yo que sólo tenemos acceso a internet para documentar nos es de gran ayuda gente como tu . A aprovecho para hacerte una consulta y es que a mi en la línea 18 posición 10 del fichero lighttpd al reiniciar la raspberry de da error yo lo he tomado directamente y lo copio de la web y lo pego en editor nano.
    Un saludo y gracias

  3. Enrique dice:

    Hola!!

    En primer lugar muchas gracias por tu aporte, me ha sido de gran utilidad.

    Y tengo una dudilla a ver si me lo puedes solucionar. Resulta que estoy intentando acceder a la raspberry de modo remoto. Y la página web la he desviado a una ip pública. La página no tiene problemas, se visualiza perfectamente. Pero en el momento que intento encender un led de un puerto no me hace caso, al no ser que lo haga desde la misma raspberry.

    Sabes alguna solución?

    Gracias!!!

    • admin dice:

      Hola!!

      Ese problema recuerdo que me pasó a mi también cuando monté la aplicación. Desafortunadamente hace ya tiempo que lo hice y no recuerdo bien como lo solucioné. Al final el problema creo que estaba relacionado con los permisos del servidor web, porque el servidor web no tenía privilegios suficientes para acceder al puerto serie. Creo que mi solución fue darle permisos al usuario del servidor web para poder acceder al puerto, aunque habría que tener cuidado con temas de seguridad, especialmente si tienes todo el sistema en una web abierta.

      Espero que eso te ayude en algo a buscar una solución al problema.

  4. Enrique dice:

    Hola Roberto.
    En primer lugar agradecerte tu respuesta.

    He estado buscando opciones para conseguir esto y después de todo he visto que mediante php es posible ejecutar scripts de python, así que voy a ponerme a hacerlo y cuando lo tenga escribiré aquí para que todo el mundo encuentre la solución

    Muchas gracias y un saludo!!

  5. andres dice:

    Hola estoy realizando el control de un Arduino a través de la RASPI pero cuando corro el código en Python me aparece un error que dice:
    Traceback (most recent call last):
    File “serial.py”, line 1, in
    import serial
    File “/root/Software/serial.py”, line 5, in
    ser=serial.Serial(‘/dev/ttyACM0’,9600)
    AttributeError: ‘module’ object has no attribute ‘Serial’

    y no sé cómo solucionarlo te agradecería mucho si me echas una mano

  6. ivan dice:

    me sale esto en el codigo python

    Traceback (most recent call last):
    File “acuario1.py”, line 13, in
    ser.write(arguments[‘port’].value)
    File “/usr/lib/python2.7/cgi.py”, line 541, in __getitem__
    raise KeyError, key
    KeyError: ‘port’

  7. ana dice:

    Muchas gracias por compartir tan buen trabajo, lo estoy intentando hacer pero el script de control me da un error, tu que crees que puede ser?
    ser.wite(arguments[‘port’].value)
    file “/usr/lib/python2.7/cgi.py”,line 541, in _getitem: raise Keyerror,key
    keyError:’port’

    gracias

  8. Matias dice:

    Muy buen post! te agradezco la guia, ojala puedas aportar mas proyectos tuyos, saludos.

  9. federgbux dice:

    Buenisimo tu articulo, felicidades, uno de los pocos articulos q vi relacionado al tema…

    Quisiera consultarte que modificaciones podria hacer para realizar tu mismo proyecto, pero con la salvedad de que en vez de visualizar el estado de los puertos, pudiera controlar un servo…

    Saludos.

  10. Claudio dice:

    Hola, lei el post muy bueno, yo hice algo similar usando un servidor apache, php, python en raspberry y arduino.

    Logro controlar el encedido de luces diverso, tengo puesto unos sensores y lo que necesito es recuperar la información de los sensores y mostrarla en la web, no quiero usar un shield ethernet, ya que seria mas complejo.

    tengo conectada la ras con arduino via usb usando screen por lo cual tengo una consola en ras que conecta a ambos.

    me pueden guiar un poco en eso para saber que hacer? desde ya agradecido

  11. armando dice:

    Hola rdiaz.
    Oye tengo una duda.Lo cierto es que soy nuevo en esto de la Raspberry. El caso es que al momento de correr el arduino.py me dice lo siguiente:

    File “arduino.py ” , line 12 in
    ser.write(arguments[‘port’].value)
    raise keyError, key
    keyError: ‘port’

    Agradeceria mucho me auxiliaras en esta parte. de antemano muchas gracias y felicidades

Deja un comentario

    Sobre este Sitio

    Definir en pocas palabras de que va esta web es un poco complicado. En esta página encontrarás un poco de todos los temas que me interesan e inquietan tanto a nivel personal como profesional. Seguramente encontrarás bastante contenido relacionado con tecnología y alguna que otra entrada relacionada con música y fotografía.