Archivo

Entradas Etiquetadas ‘Comunicación’

Dialogando con HardwareSerial y SoftwareSerial más fácil

Lunes, 29 de Agosto de 2011 Gaspar Fernández 2 comentarios

A la hora de dialogar con los Serials en Arduino, durante estos días he desarrollado funciones para leer cadenas completas de texto desde el Serial y para escribir con la sintaxis de printf(), ya que esto es mucho más fácil cuando se trata de formatear texto.

Bien, ahora se trata de unirlo todo y de dar soporte a cualquier Serial, ya sea HardwareSerial o SoftwareSerial sin complicarnos mucho la vida, con la posibilidad de cambiar esta entrada/salida en cualquier momento y así hacer nuestro programa más flexible.

Para ello he creado la biblioteca SerialExt:
SerialExt.h

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
#ifndef SERIALEXT_H
#define SERIALEXT_H

/* Quitar comentario si no vamos a usar SoftwareSerial, así
 reducimos un poco el tamaño del ejecutable. */

/* #define SEXT_NOSOFTWARESERIAL */

#include <WProgram.h>
#include <stdio.h>
#include <stdarg.h>

/* Si no vamos a usar el Software Serial, no creamos soporte para el */
#ifndef SEXT_NOSOFTWARESERIAL
#include <dynmem.h>
#include <SoftwareSerial.h>
#endif

class SerialExt
{
public:
  ~SerialExt();
  SerialExt(const HardwareSerial &serial, long bps=19200);
  #ifndef SEXT_NOSOFTWARESERIAL
  SerialExt(uint8_t rxp=2, uint8_t txp=3, long bps=19200);
  #endif
  int readString (char *str, unsigned size, const char *stop="\0");
  void printf(const char *fmt,...);
  // A veces, podemos necesitar esto desde fuera
  void serialPrint(const char *txt);
private:
  bool serialAvailable();
  int serialRead();
  HardwareSerial *hs;
  #ifndef SEXT_NOSOFTWARESERIAL
  SoftwareSerial *ss;
  #endif
  int timeout;
};

#endif

SerialExt.cpp

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
91
#include "SerialExt.h"

void SerialExt::serialPrint(const char *txt)
{
  if (hs)
    hs->print(txt);
  #ifndef SEXT_NOSOFTWARESERIAL
  else
    ss->print(txt);
  #endif
}

void SerialExt::printf(const char *fmt,...)
{
  char tmp[128]; // resulting string limited to 128 chars
  va_list args;
  va_start (args, fmt );
  vsnprintf(tmp, 128, fmt, args);
  va_end (args);
  serialPrint(tmp);
}

SerialExt::SerialExt(const HardwareSerial &serial, long bps)
{
  hs=(HardwareSerial*)&serial;

  #ifndef SEXT_NOSOFTWARESERIAL
  ss=NULL;
  #endif
  timeout=3000000/(bps/8);  // ( 1000/(bps/8) ) * 1000 * 3.0 (milisegundos por signo por 1.5)
  // Initialize serial
  hs->begin(bps);
}

#ifndef SEXT_NOSOFTWARESERIAL
SerialExt::SerialExt(uint8_t rxp, uint8_t txp, long bps)
{
  ss=new SoftwareSerial(rxp, txp);
  hs=NULL;
  // Initialize serial
  ss->begin(bps);
}
#endif

SerialExt::~SerialExt()
{
  #ifndef SEXT_NOSOFTWARESERIAL
  if (ss)
    delete ss;
  #endif
}

// Sólo aplicable con HardwareSerial
bool SerialExt::serialAvailable()
{
  if (hs)
    return hs->available();
  else
    return true;
}

int SerialExt::serialRead()
{
  if (hs)
    hs->read();
  #ifndef SEXT_NOSOFTWARESERIAL
  else
    ss->read();
  #endif
}

int SerialExt::readString(char *str, unsigned size, const char *stop)
{
  unsigned i=0;
  char sIn;
  unsigned long m;
  // Queremos que la cadena se rellene hasta size-2 para que en el carácter
  // size-1 (el último) podamos meter el terminador \0
  --size;      
  while (serialAvailable() && i<size)
    {
      sIn=serialRead();
      if (strchr(stop, sIn))
    break;
      str[i++]=sIn;
      m=micros();
      while (!serialAvailable() && micros()<m+timeout);
    }
  str[i++]='\0';
  return i;
}

Esta biblioteca, hace uso de dynmem, para poder utilizar new y delete y crear el objeto SoftwareSerial. Aunque si no vamos a utilizar el Serial por Software, hay una directiva de pre-procesador (#define SEXT_NOSOFTWARESERIAL) que si quitamos el comentario no compilará nada del soporte, con lo que ahorraremos unos 600 bytes en el binario (que a veces, pueden salvarnos la vida).

Para probar la biblioteca, cargamos el siguiente programa de ejemplo:
serial1.h

1
2
3
4
5
6
7
/* En este archivo veremos la configuración de nuestro proyecto */

#define SERIAL_SPEED 19200
/* Led que queremos que parpadee mientras se ejecuta el programa */
#define STATUS_LED   11
/* Led que indica transferencia de datos */
#define BLINK_DELAY  500

serial1.pde:

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
// serial1
// Hace un eco con el puerto serie del Arduino
// Dialogamos con el Serial a través de la clase SerialExt. Desde aquí podremos dialogar
// con cualquier HardwareSerial o SoftwareSerial

#include "serial1.h"
#include <dynmem.h>
#include <SerialExt.h>
#include <SoftwareSerial.h>

#define MAX_BUFFER 100

// Almacenamos el estado como variable global
int estado=LOW;
// Almacenamos también el número de milisegundos anterior
unsigned long momento_anterior=0;
unsigned long bytes_recibidos=0;
// SerialExt SExt(Serial);

SerialExt *SExt;

void setup()
{
  // Queremos que un led parpadee mientras trabajamos
  pinMode(STATUS_LED, OUTPUT);
  digitalWrite(STATUS_LED, HIGH);
  delay(1000);
  digitalWrite(STATUS_LED, LOW);
  delay(1000);
  SExt = new SerialExt(Serial);
  digitalWrite(STATUS_LED, HIGH);
}

void loop ()
{  
  int recibe;
  unsigned long momento_actual=millis();
  char buf[MAX_BUFFER];
// No bloqueante, si hay algo para leer entramos, si no, no.
  if(Serial.available())
    {
      SExt->readString(buf, MAX_BUFFER);
      // Escribimos el buffer completo
      SExt->printf("Texto recibido: %s\n", buf);
    }
  // No usamos delay para el parpadeo porque nos entorpece la comunicación con el serial
  if (momento_actual-momento_anterior>=BLINK_DELAY)
    {
      // Cambiamos el estado siguiente. Si era HIGH (1) ahora será
      // LOW (0). He leído en algún lado que el valor de HIGH no
      // siempre es 1; pero en este caso sí es así.
      estado=!estado;
      // Escribimos el estado actual del led
      digitalWrite(STATUS_LED, estado);
      // Establecemos el momento anterior como actual.
      momento_anterior=momento_actual;
    }
}

Como vemos, SerialExt se encarga de hacer Serial.begin() y todo (tampoco es que sea gran cosa, pero nos ahorra una línea); en este ejemplo podemos ver cómo podemos intercambiar líneas completas de texto con Arduino a través del puerto serie.

Finalizó el Tuenti Contest, ¿qué os pareció?

Miércoles, 22 de Junio de 2011 Gaspar Fernández Sin comentarios

img

Empezó siendo un comentario de mi novia, ya que al entrar en Tuenti, unos días antes de que se llevara a cabo aparecía al entrar, y yo, que como veis me encanta este mundo, me apunté. Al principio fue con la intención de coger ejemplos para las clases particulares, que muchas veces me encuentro con alguna duda puntual de un alumno y me quedo sin ejemplos.

Lo que pretendía, ingenuo de mí, era saltarme todos o casi todos los retos, copiar las preguntas y los resultados, para hacerlos tranquilamente en cuanto tuviera algo más de tiempo. Ya investigaría si me viene un algoritmo raro y los desarrollaría tranquilamente para este blog.

Por parte de la organización del concurso se facilitaban una serie de programas para probar los programas realizados y para enviarlos, es normal que poner espacios de más o textos que no son exactos a la solución esperada sean rechazados, es un sistema automático, esto ha sido una de las quejas que he visto por ahí, es verdad que podrían haber hecho un trim() para los espacios del final, pero bueno, nos lo han dejado a nosotros. Además, una vez subías una respuesta o te saltabas un reto, no había vuelta atrás.

Pero cuando el día antes de empezar publican un ejercicio de prueba de “lo que íbamos a encontrarnos”, me puse a hacerlo durante el desayuno, me pareció muy curioso lo que planteaban, me gustó la sensación al ver los OK! al probar mi código.

Al día siguiente, entré para ver cómo era todo, para no hacer muchos retos y bajarme contenidos, pero vi el segundo, y me puse a hacerlo, aunque desde el punto de vista de un profesor de programación, intentando utilizar órdenes básicas y que no asusten mucho a estudiantes y en C, y así el tercero, y el cuarto, y cuando menos me lo esperaba me di cuenta de que llevaba 7 retos hechos y tenía que dejarlo durante un rato.

Pero bueno, más allá del pique que me produjo el desarrollo de los problemas encontré varios inconvenientes:

  • Las pruebas que proponían en la página web del concurso no se parecían en nada a las pruebas de la fase de test y éstas tampoco se parecían a las pruebas que se realizaban a la hora de subir el código, cada vez eran pruebas más duras, y algún programa que hice aunque superó la prueba del test, no superó la prueba al subir.
  • Cada vez explicaban menos qué había que hacer y las pruebas propuestas eran incompletas o erróneas, lo cual dificultaba un poco la realización de los retos, aunque sí, por otra parte, agudizaba nuestro pensamiento ingeniero.
  • La comunicación por parte del usuario de Twitter de Tuenti (@TuentiEng) era más bien poca. Aunque comprendo que tampoco se puede contestar a todo y a todos.
  • Los últimos retos eran frustrantes, en el 17, por ejemplo, sólo decía “Just do it” e imposibles, bueno, no tanto, porque hay personas que los superaron.

Un detalle curioso es que la comunicación por parte de Tuenti se hacía a través de Twitter y, aunque puede parecer contradictorio, yo creo que tiene toda la lógica del mundo, hay más personas con Twitter que con Tuenti, y por Twitter la comunicación es más fluida, aunque también había un muro en la red social organizadora pero no tenía ni la décima parte de actividad.

Por último me gustaría felicitar a todos aquellos que habéis completado el concurso (yo me quedé por el 20, aunque el 19 no me terminó de salir bien, lo mio no es la criptografía, aunque poco a poco iré aprendiendo) y destacar aspectos muy positivos de este concurso:

  • He aprendido cosas muy interesantes, nos piden implementar problemas que no se suelen implementar en una carrera universitaria, y si se han llegado a implementar, las pruebas que debían pasar los algoritmos, no son tan duras en muchos casos.
  • Tengo una comunidad en Twitter más amplia con nuevos contactos muy interesantes, sobre todo de participantes.
  • La experiencia ha sido muy positiva. Participé activamente en el concurso y eso que sólo iba a seguirlo por encima para copiar los enunciados.

Actualización: Una cosa más, estoy preparando un post recopilatorio con todas las soluciones que encuentre.

Skype + Webcam en Linux Mint / Ubuntu 64bit

Lunes, 11 de Abril de 2011 Gaspar Fernández 2 comentarios

skype-webcam_recorteLa semana pasada, el 6 de abril salió la nueva versión Beta de Skype 2.2 para Linux. Aunque esta versión esté a años luz de la versión para Windows (aunque por otra parte lo prefiero, no quiero que recarguen tanto el programa), había algunas cosas básicas que deberían haber hecho hace tiempo, sobre todo en el campo de la compatibilidad con la cámara web, por ejemplo.

El programa no es libre, ni piensa serlo, aunque el protocolo de voz de skype es de lo mejor que he visto, ya que permite la comunicación con mucha compresión, más nítida que con otros medios y un ancho de banda similar. Y aunque no se les vea muy comprometidos, hay que darles la enhorabuena por hacer su software para esta plataforma, y  al menos permitir comunicarnos con otros usuarios de skype.

Hasta el momento, para mí había varios problemas:

  • Con ALSA, a veces quería enviar más información de la cuenta o menos de la que se espera, y ALSA se quejaba, dejaba de escucharse un tiempo. Skype decía que era culpa de ALSA, ALSA, que era culpa de skype…
  • Con Pulseaudio a veces se perdía el sonido, era fallo de skype, pero bueno, se reinicia y no pasa nada.
  • Webcams:
    • Con una gráfica Intel, olvídate de que se vea tu webcam (es raro, pero ni ves ni envías)
    • Con otra gráfica, pero un SO de 64bit, olvídate también, no se podrá acceder a la webcam.

En esta nueva versión han hecho cambios en esos aspectos, dicen que han mejorado el audio con Pulseaudio, y al no poder provocar el error no lo he podido probar todavía, y han mejorado el soporte de webcams. Con un equipo con gráfica Intel no he probado, pero sí en 64bit.

Para funcionar con la webcam en 64bit (con ciertas cámaras, por ejemplo la OV511/519, yo tengo una Creative muy vieja, también se repite el problema con algunas Genius y algunas Logitech). Primero tendremos que instalar los módulos de compatibilidad de Video4Linux de 32 bit.

$ sudo apt-get install lib32v4l-0

Y a la hora de ejecutar skype:

$ LD_PRELOAD=/usr/lib32/libv4l/v4l1compat.so skype

Esto ya funcionaba en la Beta 2.1 con algunas cámaras, pero no con todas. Ahora funciona con algunas cámaras más.

Podremos configurar el acceso directo a skype para que ejecute también el LD_PRELOAD.

Ahora mismo, dejo las instrucciones para Linux Mint / Ubuntu, aunque en cualquier distribución podremos instalar las librerías Video4Linux de 32bit y precargarla antes de ejecutar skype.

Intercambiar mensajes en red local [ solución rápida con netcat y kdialog ]

Sábado, 23 de Octubre de 2010 Gaspar Fernández Sin comentarios

A veces tenemos la necesidad de, en la propia red local intercambiar algún mensaje de texto, ya sea una dirección web o algo así. Muchas veces se opta por enviar un e-mail, aunque aquí traigo una propuesta interesante. No para uso diario, pero para uso puntual cuando no tenemos nada a mano, o para estudiar el método.

Actualmente usa netcat y KDE (para la petición de datos de forma gráfica).

Con netcat sólo bastaría. En un ordenador ponemos:

$ netcat -l -p 6517

Actuaría como servidor escuchando por el puerto 6517 (he usado este puerto, porque creo que no hay ningún servicio definido en este lugar), luego en otro ordenador podemos escribir:

$ netcat [IP/host del primer ordenador] 6517

Con lo que conectaríamos con el otro ordenador. Es una conexión no cifrada, con lo que en este punto no estamos hablando de seguridad, sólo de un “apaño”.

Pero bueno, aunque sea un apaño no tiene por qué ser demasiado feo, así que vamos a acompañarlo de unos diálogos (con kdialog) y a hacer unos scripts que nos permitan iniciar netcat de forma fácil (incluso vincularlo a una tecla de nuestro sistema).

[ enviamensaje ]

1
2
3
4
5
host=`kdialog --inputbox "A que host quieres enviar un mensaje"`
mensaje=`kdialog --inputbox "Escribe el mensaje"`

# Puerto 6517 sin asignar
echo "$mensaje" | netcat -c $host 6517

Desde este archivo preguntamos al usuario el host con el que quiere hablar, y luego se le pide introducir el mensaje, que se mandará por netcat ( -c vale para que la conexión se cierre cuando se termine de enviar el mensaje )

[ recibemensaje ]

1
2
3
4
5
6
7
8
9
10
while read input
do
        kdialog --textinputbox "Alguien te manda algo" "$input" 2>&1 1>/dev/null;
        if [[ "$?" != "0" ]]
        then
                break;
        fi
done

echo "FIN DEL PROGRAMA"

Con este script escribiremos en un diálogo con kdialog el texto recibido por el mensaje. netcat nos pasará por su salida estándar el contenido del mensaje, así que tendremos que leerlo con read

Si aceptamos la ventana dejaremos de esperar mensajes.

[ servidormensajes ]

1
2
3
4
5
6
7
8
9
while (( 1 ))
do
        netcat -l -p 6517 -e ./recibemensaje
        kdialog --yesno "Desconectar servidor?"
        if [[ "$?" == "0" ]]
        then
                break;
        fi
done

Desde aquí ponemos el socket en escucha ( -l = listen ) en el puerto 6517 y lo que recibamos se lo pasamos al script recibemensaje; una vez que esto termine nos preguntará si queremos cerrar el servidor o estamos esperando mensajes de alguien más.

Ahora sólo tenemos que vincular a alguna tecla los script servidormensajes y enviamensaje con lo que podremos comunicarnos con las personas de nuestra red local sin problema. El programa también funcionará por Internet, si tenemos los puertos necesarios abiertos y redireccionados en el router (si usamos router) o nuestro firewall no restringe las conexiones por el puerto 6517 (aunque podemos cambiarlo).

Tuberías con nombre para comunicación entre procesos

Domingo, 19 de Julio de 2009 blakeyed 2 comentarios

Las tuberías con nombre son un método de comunicación FIFO entre procesos (también se les llama fifos). FIFO son las siglas de First In First Out, es decir, el primero que llega es que primero que se marcha, como en una cola, el primero que llega es el que antes termina.

Observad lo que hacen las macros depura_int y depura_string, nos darán el número de línea y el archivo donde están, así como el nombre de la variable y el valor que tiene en ese momento.

Normalmente las tuberías son anónimas, aunque el hecho de tenerlas con un nombre en nuestro sistema de ficheros puede sernos de gran utilidad. Como ejemplo, podemos verlo en funcionamiento en la siguiente imagen:

Cómo hacer una pipe con nombre en 1 minuto

Cómo hacer una pipe con nombre en 1 minuto

Primero vemos el terminal de abajo, en el que se ejecuta lo siguiente:

1
2
$ mkfifo prueba
$ cat prueba

Seguidamente en el otro terminal se escribe lo siguiente:

1
$ echo "Esto es una prueba" > prueba

Automáticamente el texto “Esto es una prueba” aparece en el primer terminal. ¡Hemos logrado enviar un mensaje!

De la misma forma podemos programar aplicaciones que utilicen estas características de forma muy fácil en casi cualquier lenguaje de programación, para los ejemplos utilizaré C. Debemos hacer dos programas, uno que escriba en la tubería (pipe) y otro que lea de ella. Empezamos por pipewrite.c:

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
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
  FILE *mipipe;
  char buffer[128];
  int i=0;

  if (mkfifo("pipetest", S_IWUSR |  /* El usuario puede escribir */
                 S_IRUSR |  /* El usuario puede leer */
                 S_IRGRP |  /* El grupo puede leer */
                     S_IROTH    /* Otros pueden leer */
         )!=0)
    printf ("Hubo un problema al crear la pipe\n");

  mipipe=fopen("pipetest", "w"); /* Lo abrimos como un fichero normal y corriente */

  /* Con esta línea leemos hasta que se cierre la tubería por el otro */

  while (i<10)
    {
      sprintf(buffer, "CADENA ENVIADA Número: %i\n", i+1);
      fputs(buffer, mipipe);
      i++;
    }

  fclose(mipipe);       /* Cerramos la tubería por aquí también */

Y ahora pipecilla.c (o el programa que leerá la pipe):

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
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
  FILE *mipipe;
  char buffer[128];
  if (mkfifo("pipetest", S_IWUSR |  /* El usuario puede escribir */
                 S_IRUSR |  /* El usuario puede leer */
                 S_IRGRP |  /* El grupo puede leer */
                     S_IROTH    /* Otros pueden leer */
         )!=0)
    printf ("Hubo un problema al crear la pipe\n");

  mipipe=fopen("pipetest", "r"); /* Lo abrimos como un fichero normal y corriente */

  /* Con esta línea leemos hasta que se cierre la tubería por el otro */
  while (!feof(mipipe))      
    {
      if (fgets(buffer, 128, mipipe))
      printf ("RECIBIDO: %s", buffer);
    }

  fclose(mipipe);       /* Cerramos la tubería por aquí también */
}

Ahora compilamos y ejecutamos en terminales diferentes de nuevo:
Probando pipes
En el terminal de pipewrite.c, al ejecutar el programa da un mensaje de error, es porque la pipe ya existe (se creó en el programa anterior).

Esta forma de comunicación es unidireccional, puede ser últil por ejemplo para publicar el estado de un programa (un reproductor de audio podría publicar la canción que está sonando como en xmms-infopipe), o para modificar el comportamiento de un software mientras éste está en ejecución. Pero si queremos que nuestras aplicaciones dialoguen (en full-duplex), debemos tener dos pipes simultáneas, en ese caso, la aplicación 1, por ejemplo deberá leer en la tubería 1 y escribir en la tubería 2; y la aplicación 2, deberá escribir en la tubería 1 y leer en la tubería 2.

Visita otras webs de la red