15 de febrero de 2016

Arduino y Processing 3: Mostrar los datos de dos sensores al mismo tiempo

Previamente, hemos visto enviar información desde Arduino a Procesing de una forma muy básica y, posteriormente, habíamos aprovechado la comunicación por puerto serie probando a controlar la dirección de caída de unas gotas de lluvia con un potenciómetro. Si deseáis revisar esas dos entradas, podéis encontrarlas en los siguientes enlaces:


En esta tercera entrega, veremos cómo recibir varios datos simultáneamente por nuestro puerto serie y los mostraremos gráficamente como si de un osciloscopio se tratase.

Para los datos, he decidido colocar dos fotorresistores en nuestra Arduino basándome en una entrada antigua en la que explicaba cómo utilizar un sensor de luz para controlar el brillo de una LED. Podéis leer esa entrada en el siguiente enlace:


El nivel de luz que recibamos en cada uno de los dos sensores, será el que enviemos por puerto serie hacia Processing y lo representaremos gráficamente. He aquí una imagen de ejemplo de cómo deberíamos verlo:


Empezaremos por preparar la Arduino y colocar los dos sensores. Las resistencias que he usado para las LEDs son de 330Ω y las de los sensores son de 10kΩ. Así es cómo he colocado los componentes:
Imagen creada con Fritzzing

Tal como había hecho en la entrada en la que se explicaba cómo usar un sensor de luz, he colocado una LED para cada sensor, de tal forma que, cuanta menos luz detecte, más brillo le daré a la LED correspondiente.

/* Definimos pines */
#define PIN_LED1 9   // Conectamos una LED en el pin 9
#define PIN_LED2 10  // Conectamos una LED en el pin 10
#define PIN_LUZ1 A0  // Conectamos un fotorresistor en el pin analógico A0
#define PIN_LUZ2 A5  // Conectamos un fotorresistor en el pin analógico A5

String dato;         // Para enviar un dato por el puerto serie

void setup()
{
  /* Establecemos los pines de las LED como salida */
  pinMode(PIN_LED1, OUTPUT);
  pinMode(PIN_LED2, OUTPUT);
  
  /* Establecemos los fotorresistores como entrada */
  pinMode(PIN_LUZ1, INPUT); 
  pinMode(PIN_LUZ2, INPUT); 
  
  /* Iniciamos el puerto serie */
  Serial.begin(9600);
}

void loop()
{
  /* Obtenemos valores de los fotorresistores (varían de 0 a 900) */
  int nivelDeLuz1 = analogRead(PIN_LUZ1);  // fotorresistor1
  int nivelDeLuz2 = analogRead(PIN_LUZ2);  // fotorresistor2
  
  /* Escalamos los valores para el brillo de las LED */
  nivelDeLuz1 = map(nivelDeLuz1, 0, 900, 0, 255);
  if(nivelDeLuz1 < 15){ // si el valor es muy bajo...
    nivelDeLuz1 = 0;    // ...lo hago 0 directamente
  }
  
  nivelDeLuz2 = map(nivelDeLuz2, 0, 900, 0, 255);
  if(nivelDeLuz2 < 15){
    nivelDeLuz2 = 0;
  } 
  
  /* Nos aseguramos de que los niveles de luz son correctos */
  nivelDeLuz1 = constrain(nivelDeLuz1, 0, 255);
  nivelDeLuz2 = constrain(nivelDeLuz2, 0, 255);   
  
  /* Aplicamos el brillo a las LED */
  analogWrite(PIN_LED1, nivelDeLuz1);
  analogWrite(PIN_LED2, nivelDeLuz2);
  
  /* Enviamos dato por puerto serie */
  dato = "";
  dato += nivelDeLuz1;
  dato += ",";
  dato += nivelDeLuz2;
  Serial.println(dato);
  
  delay(50); // pequeña pausa para no saturar el puerto serie
}


En general, el código parece fácil de entender. Debemos de tener en cuenta el uso la función constrain para evitar valores no deseados.

Supongamos el código siguiente:

  DATO = constrain(X, A, B);   

Si el valor de X es menor que A, el valor de DATO será A. Si el valor de X es mayor que B, el valor de DATO será B. Y, finalmente, si el valor de X está entre A y B, el valor de DATO será X. Con ello, nos aseguramos que el valor de DATO estará siempre entre A y B.

Para enviar el dato por el puerto serie, lo haremos con una variable tipo String. Como vemos al final del código, la inicializamos como vacía y luego la "construimos" poco a poco añadiendo los dos niveles de luz separados por una coma ",". Eso lo usaremos posteriormente para separarlos al recibirlos en Processing.

Tal como está ahora mismo nuestra Arduino, ya podemos comprobar el funcionamiento correcto de los sensores fijándonos en las dos LED y abriendo el Monitor Serie del IDE Arduino:


Una vez comprobado el correcto funcionamiento de nuestra Arduino, vamos con Proccessing.

Al igual que en la entrega anterior, tendremos dos archivos: uno para el programa principal, y otro con una clase. Podemos verlo a continuación:


Empezamos por la clase Punto, que nos servirá para gestionar cada uno de los puntos de la gráfica y el desplazamiento hacia la izquierda.

Este es el código que he utilizado:

class Punto {
  float x;        // Coordenada horizontal
  float y;        // Coordenada vertical
  int velocidad;  // Píxels que desplazo a la izquierda en cada movimiento
  
  Punto(){
    x = width;
    y = height / 2;
    velocidad = 5;
  }
  
  /**
   * @param c_vertical Coordenada vertical
   */
  Punto(float c_vertical){
    this();  
    setY(c_vertical);
  }
  
  
  /**
   * @param c_horizontal Coordenada horizontal
   * @param c_vertical Coordenada vertical
   */
  Punto(float c_horizontal, float c_vertical){
    this();
    setX(c_horizontal);
    setY(c_vertical);
  }
  
  /**
   * Desplaza el punto hacia la izquierda
   */
  void desplazar(){
    setX(x - velocidad);
  }
  
  /**
   * Dibuja el punto
   */
  void dibujar(){
    ellipseMode(CENTER);
    ellipse(x, y, 2, 2);
  }
  
  /**
   * Establece el valor de la coordenada horizontal del punto
   * @param nuevoX La nueva coordenada horizontal
   */
   void setX(float nuevoX){
     x = nuevoX;
   }
   
  /**
   * Establece el valor de la coordenada vertical del punto
   * @param nuevoY La nueva coordenada vertical
   */
   void setY(float nuevoY){
     y = nuevoY;
   }
   
  /**
   * Establece la velocidad de movimiento hacia la izquierda del punto
   * @param velocidad La nueva velocidad
   */
   void setVelocidad(int v){
     velocidad = v;
   }
  
  
  /**
   * Devuelve el valor de la coordenada horizontal del punto
   * @return x La coordenada horizontal
   */
   float getX(){
     return x;
   }
   
  /**
   * Devuelve el valor de la coordenada vertical del punto
   * @return y La coordenada vertical
   */
   float getY(){
     return y;
   }
   
  /**
   * Devuelve la velocidad de movimiento hacia la izquierda del punto
   * @return velocidad La velocidad
   */
   int getVelocidad(){
     return velocidad;
   }
}

Hay algún método que no utilizo, como por ejemplo "dibujar()", que dibujaría un punto en la posición del punto (valga la redundancia). Si posteriormente hacéis alguna modificación en el archivo principal para experimentar, puede seros de ayuda tener esos métodos creados.

A continuación, podemos ver el código que he utilizado para el archivo principal:

import processing.serial.*;   // Para usar el puerto serie

ArrayList<Punto> onda1;       // Conjunto de puntos para representar la onda1
ArrayList<Punto> onda2;       // Conjunto de puntos para representar la onda2
int valores[] = {0, 0};       // Array para obtener los dos valores del puerto serie
float altura[] = {0.0, 0.0};  // Array para las alturas recibidas

Serial puerto;                // Puerto serie

void setup(){
   /* Configuración de ventana */
  size(800, 600);             // Tamaño de ventana 
  frameRate(30);              // FPS
  smooth();                   // Suavizado activado  
  
  /* Inicialización de variables */
  onda1 = new ArrayList<Punto>();
  onda2 = new ArrayList<Punto>();
  
  /* Configuración de puerto serie */
  String nombrePuerto = Serial.list()[0];
  println("Puerto establecido a " + nombrePuerto + ".");
  puerto = new Serial(this, nombrePuerto, 9600);
  puerto.bufferUntil('\n');   // guardo datos hasta que encuentro salto de línea
}

void draw(){
  background(0);              // Pintamos color de fondo
  
  if(valores != null){
    altura[0] = map((float) valores[0], 0, 255, 0, height);
    altura[1] = map((float) valores[1], 0, 255, 0, height);
    
    onda1.add(new Punto(altura[0]));
    onda2.add(new Punto(altura[1]));
    
    fill(#FF0000);           // relleno color rojo (para el texto) 
    text("Valor 1: " + altura[0], 20, 30);
    fill(#FFFF00);           // relleno color amarillo (para el texto) 
    text("Valor 2: " + altura[1], 20, 50);
    
    noFill();                // sin relleno
    strokeWeight(2);         // grosor del lápiz
    stroke(#FF0000);         // color del lápiz
    dibujaCurva(onda1);
    stroke(#FFFF00);         // color del lápiz
    dibujaCurva(onda2);
  }
}

void dibujaCurva(ArrayList<Punto> onda){
  beginShape();                               // empieza curva
  for(int i = 0; i < onda.size(); i++){
    Punto punto = onda.get(i);
    punto.desplazar();       
    curveVertex(punto.getX(), punto.getY());  // añado un punto a la curva
    
    // punto.dibujar();                       // dibujo el punto
    
    /* Borrar punto si se ha desplazado demasiado a la izquierda */
    if(punto.getX() < -width){
      onda.remove(i);
    }
  }
  if(onda.size() > 0){
    curveVertex(onda.get(onda.size() - 1).getX() + 1, onda.get(onda.size() - 1).getY());
    line(onda.get(onda.size() - 1).getX(), onda.get(onda.size() - 1).getY(), width, onda.get(onda.size() - 1).getY());     
  }
  /* Dibujar curva */
  endShape();                                   // termina curva 
}

void serialEvent(Serial p){
  String dato = p.readStringUntil('\n'); // leemos el buffer del puerto serie
  
  if(dato != null){
    dato = trim(dato); 
    valores = int(split(dato, ',')); 
  }
}

La gran diferencia respecto a cómo recogíamos datos del puerto serie en la primera y segunda entradas, es que ahora hacemos uso de serialEvent. En vez de escuchar permanentemente el puerto en la sección draw(), ahora guardamos lo recibido en el puerto serie ya en la sección setup() usando bufferUntil y luego en la nueva sección de serialEvent es cuando recogemos el dato propiamente dicho.

Como valores es un array, utilizamos la función split para separar los valores contenidos en dato y que están separados por el símbolo coma ",", tal como vemos al final del código:

    valores = int(split(dato, ',')); 

Para dibujar la curva, enviamos el conjunto de puntos al método dibujaCurva y hacemos uso de curveVertex.

Para dibujar una curva dando los puntos por los que pasa, primero hacemos una llamada a beginShape() para indicar que empezamos a dibujar. Lo siguiente es ir añadiendo puntos con curveVertex() y, para terminar, usaremos endShape(), que es cuando se dibuja la curva.

Aquí vemos un ejemplo de cómo podríamos dibujar una curva:
void setup(){
  size(240, 120);  // tamaño de ventana
}
void draw(){
  background(0);   // fondo negro
  strokeWeight(2); // grosor de lápiz
  stroke(#00FF00); // lápiz verde
  noFill();        // sin relleno
  smooth();        // suavizado
  
  beginShape();         // empieza curva  
  curveVertex(0, 20);   // añado un punto a la curva
  curveVertex(20, 20);  // añado un punto a la curva
  curveVertex(40, 60);  // añado un punto a la curva
  curveVertex(60, 20);  // añado un punto a la curva
  curveVertex(80, 60);  // añado un punto a la curva
  curveVertex(100, 20); // añado un punto a la curva
  curveVertex(120, 20); // añado un punto a la curva
  endShape();           // termino curva
}
  
Si lo ejecutamos, el resultado será el siguiente:


Como vemos, no se ha dibujado ni el primer punto ni el último, de ahí el último if que utilizo en el método dibujaCurva, para añadir un punto más y que se dibuje correctamente.

A continuación, podemos ver un vídeo con el programa en funcionamiento:


Espero que os haya gustado.

Un saludo: Roi

12 de febrero de 2016

Arduino y Processing 2: Lluvia controlada por puerto serie

En una entrada anterior habíamos visto cómo comunicarnos de una forma muy básica desde nuestra Arduino con Processing a través del puerto serie. Por si deseáis revisarla, podéis encontrarla en el siguiente enlace: Arduino y Processing 1: Comunicación por puerto serie.

Esta vez, vamos a ver cómo "dibujar" lluvia en Processing y controlar la dirección en la que cae con un potenciómetro en Arduino.

Para hacernos una idea de cómo debe funcionar, veamos el siguiente vídeo:


Aunque la calidad de imagen de la animación de Processing no ha sido la mejor, nos sirve perfectamente para entender qué deseamos hacer.

Lo primero, será preparar nuestra Arduino. Necesitamos un potenciómetro, una LED y una resistencia de 330 Ohm (o la que creáis conveniente) para la LED. El montaje que he realizado es el siguiente:

Imagen realizada con Fritzing
En realidad, la LED sólo la utilizo para ver que los datos del potenciómetro se ven correctamente, ya que la intensidad de la LED variará según el valor obtenido. Por supuesto, toda la parte relativa a la LED podría eliminarse del código, lo dejo a vuestra elección.

A continuación, veamos el código que he utilizado en la Arduino:
#define PIN_POT A0       // pin de potenciómetro (analógico) 
#define PIN_LED 9        // pin de la LED (analógico)

int valor_POT = 0;       // para guardar el valor del potenciómetro
int brillo = 0;          // brillo de la LED


void setup() {
  pinMode(PIN_POT, INPUT);  // configuro pin del potenciómetro como Entrada
  pinMode(PIN_LED, OUTPUT); // configuro pin de la LED como Salida
  Serial.begin(9600);       // inicializo puerto serie
}

void loop() {
  valor_POT = analogRead(PIN_POT);             // obtengo el valor del potenciómetro (0 a 1023)
  brillo = map(valor_POT, 0, 1023, 0, 255);    // calculo el valor del brillo de la LED
  analogWrite(PIN_LED, brillo);                // cambio el brillo de la LED
  Serial.println(valor_POT);                   // envío el valor por el puerto serie
  delay(100);                                  // pequeña pausa para no saturar el puerto
}

Si cargamos el programa a nuestra Arduino, podremos ver como el brillo de la LED aumenta y disminuye según la posición en la que pongamos el potenciómetro.

Ahora que ya tenemos preparada la Arduino, vamos con Processing.

Necesitaremos dos archivos. Uno será el que gestione la animación y la comunicación por el puerto serie y, el otro, lo usaremos para crear la clase Gota, que gestionará el movimiento de una gota y también la dibujará.


Empezamos primero por la clase Gota. Este es el código que he utilizado:

class Gota {
  float x;        // Coordenada horizontal
  float y;        // Coordenada vertical
  float longitud; // Longitud de la gota
  float theta;    // Ángulo de la gota (que variará según lo obtenido por el puerto serie)
  int velocidad;  // Píxels que aumento en cada movimiento
  
  Gota(){
    x = random(width);   // Valor horizontal aleatorio, para que salga de cualquier sitio
    y = 0;               // Las gotas caen de la parte superior
    longitud = 10;       // El largo de la gota
    theta = radians(60); // Ángulo en radianes
    velocidad = 16;      // Para controlar la velocidad a la que cae la gota
  }
  
  /**
   * @param l Longitud de la gota
   * @param a Ángulo de la gota en grados
   */
  Gota(float l, float a){
    this();              // Llamo al constructor sin parámetros, para no repetir...
    longitud = l;        // Longitud que he recibido como parámetro
    theta = radians(a);  // Ángulo que he recibido como parámetro, en radianes
  }
  
  /**
   * Desplaza la gota hacia abajo y la dibuja
   */
  void caer(){
    pushMatrix();        // guardo posición actual del "lienzo"
    x = x % width;       // si la gota llega al borde, aparece por el otro
    y = y % height;      // si la gota llega al fondo, aparece por arriba
    if(y == 0)           // si la gota está arriba de todo...
       setX(random(width)); // ...obtengo una posición horizontal aleatoria
    if(x <= 0)           // si la gota se ha ido por el borde izquierdo...
      setX(width);       // ...la coloco a la derecha del todo
    translate(x, y);     // colocamos el lienzo en la posición deseada
    rotate(-theta);      // giramos lienzo los grados que queremos
    line(0, 0, 0, longitud); // dibujamos en el lienzo una línea con el largo deseado
    x += sin(theta) * velocidad; // desplazamos la gota proporcionalmente al ángulo
    y += cos(theta) * velocidad; // desplazamos la gota proporcionalmente al ángulo
    popMatrix();         // vuelvo al estado inicial del lienzo    
  }
  
  /**
   * Establece el valor de la coordenada horizontal de la gota
   * @param nuevoX La nueva coordenada horizontal
   */
   void setX(float nuevoX){
     x = nuevoX;
   }
   
  /**
   * Establece ángulo de la gota en radianes
   * @param newAngle El nuevo ángulo en grados
   */
   void setAngle(float newAngle){
     theta = radians(newAngle);
   }
}

Ahora que ya tenemos nuestra clase Gota creada, vamos con el programa principal. La base de comunicación, la habíamos visto ya en una entrada anterior. Simplemente hemos añadido lo necesario para gestionar el dato recibido: Aquí está el código necesario:

import processing.serial.*;   // Para usar el puerto serie

ArrayList<Gota> lluvia;       // Para el conjunto de gotas
static int num_gotas = 1500;  // Para limitar el número máximo de gotas
float theta = 45;             // Ángulo
float longitud = 15;          // Largo de la gota 
Serial puerto;                // Puerto serie
String dato, dato_AUX;        // Dato recibido por el puerto serie

void setup(){
   /* Configuración de ventana */
  size(800, 600);             // Tamaño de ventana 
  background(0);              // Color de fondo
  frameRate(60);              // Fotogramas por segundo 
  
  /* Configuración de pintura */
  stroke(#A0DBFF);            // Cambiamos el color del lápiz a un tono azul 
  
  /* Inicialización de variables */
  lluvia = new ArrayList<Gota>();
  dato = "1";              
  dato_AUX = "1"; 
  
  /* Configuración de puerto serie */
  String nombrePuerto = Serial.list()[0];
  println("Puerto establecido a " + nombrePuerto + "."); // Mostrar el puerto elegido
  puerto = new Serial(this, nombrePuerto, 9600);
}

void draw(){
  background(0);              // Pintamos color de fondo
  
  if(puerto.available() > 0){            // si el puerto está disponible...
    dato = puerto.readStringUntil('\n'); //...leo hasta encontrar un salto de línea
  }
  if(dato == null){                      // Si he recibido un dato nulo...
    dato = dato_AUX;                     // ...utilizo el anterior
  } else {                               // Si el dato NO es nulo...
    if(trim(dato).equals(dato_AUX) == false){ // ...y es distinto del anterior...
      println("Ángulo: " + map((float) int(dato_AUX), 0, 1023, -90, 90));
      dato_AUX = trim(dato);             // Guardo el dato borrando los espacios en blanco
    }    
  }
  
  /* Ángulo */
  theta = map((float) int(dato_AUX), 0, 1023, -90, 90); // obtengo el ángulo (de -90º a 90º)
  
  if(lluvia.size() < num_gotas && random(10) < 1) // Si hay sitio para más gotas...
    lluvia.add(new Gota(longitud, theta));        // ...añado una nueva
  
  for(Gota gota : lluvia){ // Reviso todas las gotas del conjunto
    gota.setAngle(theta);  // Establezco el ángulo en que deben orientarse
    gota.caer();           // Las desplazo y dibujo
  }
}


Además de comprobar que no excedamos el número de gotas, también he añadido una condición que hará que no se creen todas al mismo tiempo, si no que caigan aleatoriamente. Es decir, si hay sitio para más gotas, obtengo un número aleatorio entre 0 y 9 y, si ha salido cero, entonces sí creo la gota... y si no, pues no la creo. Por eso el último "if" utiliza esa segunda condición. Si no fuera así, caerían las gotas todas al mismo tiempo y el efecto sería completamente diferente:

  if(lluvia.size() < num_gotas && random(10) < 1)
    lluvia.add(new Gota(longitud, theta));

A continuación, podéis ver una imagen del resultado final:



Os recomiendo variar varios parámetros del código para ver cómo afecta la velocidad, longitud de la gota, número de gotas... incluso los fotogramas por segundo.

En la clase Gota, en el método caer, podéis cambiar line() por ellipse(), por ejemplo, y ver cómo caen pequeños círculos en vez de líneas (aunque así no apreciaréis el ángulo). Lo divertido está en experimentar.

Espero que os haya gustado.

Un saludo: Roi.

5 de febrero de 2016

Iniciar Telegram Desktop en la bandeja de sistema en openSuse 13.2

Seguramente conocéis el programa de mensajería Telegram. Para Linux, tenemos el cliente nativo Telegram Desktop y que es muy sencillo de instalar. Tan solo tendremos que descargarlo, descomprimirlo y, al ejecutar el archivo Telegram, ya se instalará y se creará un enlace en nuestro menú para poder ejecutarlo.


Como seguramente habéis notado, cuando dejamos una aplicación abierta, al reiniciar el equipo, ésta se ejecuta automáticamente al arrancar. Esto es especialmente útil, por ejemplo, para un programa de mensajería, ya que así no tendremos que abrirlo cada vez que encendemos el equipo para poder estar "online". 

En mi caso, y tal como se puede suponer por el título, lo he instalado en openSuse 13.2 64bit con KDE.

El problema con el que nos encontramos con Telegram Desktop es que, si dejamos el programa abierto (lo más habitual), se iniciará pero en modo ventana, tal como se ve en la siguiente imagen:


Si, por otro lado, lo cerramos durante nuestra sesión, en el siguiente arranque no se cargará automáticamente, lo cual también es un problema.

Para solucionarlo, vamos a utilizar las opciones de arranque de KDE.

Lo primero que necesitamos, es ir a Preferencias del sistema, que encontraremos en Aplicaciones > Sistema:


Una vez abiertas las Preferencias del sistema, debemos pulsar en Arranque y apagado en la sección de Administración del sistema:


Se abrirá una nueva ventana. En ella, debemos pulsar en el botón Añadir programa:


Veremos ahora un listado con las aplicaciones que tenemos en nuestro menú. Tendremos que seleccionar Telegram Desktop (no tiene por coincidir con donde lo tengo yo) y pulsar el botón Aceptar:


Ahora, se nos mostrarán las propiedades del acceso. Debemos ir a la pestaña Aplicación:


En Orden, debemos modificar el final de forma que, tras la palabra Telegram, aparezca -startintray %u, para forzar que arranque en la bandeja del sistema. Por ejemplo, podría quedarnos una línea similar a esta:

/home/tuUsuario/rutaDondeHasInstaladoTelegram/Telegram -startintray %u

Tras realizar el cambio, pulsaremos Aceptar. Veamos una imagen de cómo ha quedado en mi caso:


Una vez aceptado el cambio, veremos una línea más en la sección de Autoarranque:


Con esto, hemos conseguido que, si cerramos Telegram antes de finalizar la sesión, vuelva a cargarse automáticamente al encender el equipo y, además, que lo haga en la bandeja del sistema.

Si reiniciamos ahora el equipo sin haber cerrado previamente Telegram, seguiríamos teniendo el problema de que arranca en modo ventana, ya que se restaura la aplicación por haberla dejado abierta.

Para que eso no ocurra, en la misma ventana en la que estábamos, tenemos que ir a la sección Gestión de sesiones y, en la lista de Aplicaciones a excluir de las sesiones, añadiremos "Telegram" (es importante que la T sea mayúscula) tal como vemos en la siguiente imagen:


Tras haber aplicado este último cambio, nuestra sesión no recordará el estado de la aplicación Telegram, con lo que siempre que arranquemos hará caso de lo que hemos añadido en la sección Autoarranque.

A partir de ahora, Telegram ya se iniciará con nuestro sistema y en la bandeja de sistema, tal como vemos en la siguiente imagen:


Espero que os haya sido de ayuda.

Un saludo.



4 de febrero de 2016

Arduino y Processing 1: Comunicación básica por puerto serie

Hace tiempo que había publicado una entrada de cómo comunicarnos con nuestra Arduino desde Python. Esta vez vamos a ver cómo puedo comunicarme desde Arduino con un programa realizado en Processing.

En esta explicación he utilizado el IDE Arduino 1.6.0 con una Arduino UNO, el IDE de Processing 3.0.1 y tengo todo instalado en openSuse 13.2 64bit.

Para ver cómo comunicar nuestra Arduino con Processing, empezaremos por algo muy básico. Vamos primero con la Arduino. Este es el código que he utilizado:

void setup() {
  Serial.begin(9600);

}

void loop() {
  Serial.println("Hola!");
  delay(100);
}

Como veis, es muy simple. En la sección setup, utilizo Serial.begin(9600) para inicializar el puerto serie y, en la sección loop, imprimo por el puerto serie la palabra "Hola!", seguida de una espera de 100 milisegundos para no saturar el puerto serie.

Con esto, ya tenemos nuestra Arduino enviando una y otra vez la palabra "Hola!" a través del puerto serie.

Vamos ahora con la parte de Processing.

Lo primero que necesitamos saber es en qué puerto tenemos conectada la Arduino. Para ello, primero ejecutamos el siguiente código en Processing:

import processing.serial.*; // librería necesaria para comunicación serie

void setup(){
  println(Serial.list());
}
Con esto, podremos ver en el terminal de Processing la lista de puertos de una forma similar a esta:

 
Vamos al IDE de Arduino y, en el menú Herramientas, podemos ver el puerto en la que tenemos conectada nuestra placa:


En mi caso, está en /devtty/ACM0. Si comprobamos la lista que se nos ha mostrado en Processing, el primer elemento corresponde a mi puerto. Como en el listado se numeran como 0, 1, 2... nos quedamos con que mi puerto es el número 0 de la lista.
Volvemos a Processing, borramos la línea que imprime la lista y ampliamos el código, quedando así:

import processing.serial.*;

Serial puerto; // creamos un objeto de la clase Serial
String dato;   // para los datos recibidos por el puerto serie

void setup(){
  String nombrePuerto = Serial.list()[0];
  println("Puerto establecido a " + nombrePuerto + ".");
  puerto = new Serial(this, nombrePuerto, 9600);
}

void draw(){
  if(puerto.available() > 0){
    dato = puerto.readStringUntil('\n');
  }
  println("Dato recibido: " + dato);
}
Hemos añadido dos variables, una para gestionar el puerto y otra para guardar el dato recibido.

Antes hemos averiguado qué puerto es el nuestro, ahora guardamos su nombre obteniéndolo con Serial.list()[0], donde el número 0 es el número de puerto en la lista.

El println que he utilizado, podéis omitirlo. Simplemente es para ver que el nombre elegido es el correcto.

En la sección setup, la última línea es para inicializar el puerto. Como podemos observar, le pasamos el nombre del puerto y la velocidad, que es la misma que habíamos indicado en nuestra Arduino también.

No vamos a dibujar nada en la sección draw. De momento, solamente mostramos datos en el terminal de Processing.

Con la primera línea, comprobamos si el puerto está disponible. Si sí lo está, guardamos el dato en la variable con puerto.readStringUntil('\n'), que lee todos los datos hasta que encuentra un salto de línea.

Una vez hecho esto, mostramos el dato.

A continuación, vemos una imagen de cómo ha quedado la ejecución:


Como vemos, se está recibiendo la palabra "Hola!" una y otra vez. También notaréis que, a veces nos aparece algún "null". Eso es normal, simplemente habría que tenerlo en cuenta en nuestra programación para obviarlo.

Para continuar viendo cómo comunicarnos por el puerto serie con nuestra Arduino y processing, podéis echarle un ojo a la siguiente entrega: Arduino y Processing 2: Lluvia controlada por puerto serie.

Espero que os haya sido de ayuda para poder empezar con vuestros proyectos de Arduino con Processing.

Un saludo.

11 de enero de 2016

Mad Catz R.A.T. 5 en openSuse 13.2

Recientemente he adquirido un ratón Mad Catz R.A.T. 5. Tal como había leído antes de comprarlo, ha sido enchufarlo y ver que no funcionaba como debiera en Linux y, más concretamente, en openSuse 13.2. La rueda horizontal no funcionaba, los botones laterales de adelante y atrás tampoco y, lo peor... ¡se perdía el foco del ratón y no podía hacer clic!


Para poder hacer clic, tenía que hacer varios clics con el secundario y el principal hasta que en algún momento se reestablecía el foco pero claro, trabajar así es inviable.

Basándome en la configuración del R.A.T. 7 que encontré en el artículo "Fixing the Mad Catz R.A.T 7 mouse under openSUSE / Linux" del blog de Andre Ritcher, la configuración de ratones Mad Catz de la wiki de Archlinux y la configuración de un R.A.T. 5 en la comunidad de Linux Mint, he conseguido que funcionen tanto el foco, como los botones adelante y atrás y la rueda horizontal.

Vamos a ver cómo podemos hacerlo paso a paso.

Lo primero que necesitamos, es abrir el gestor de archivos en modo superusuario. Para ello, vamos al Lanzador de aplicaciones y, en la sección Administrador de archivos que encontramos dentro de Sistema, pulsamos en Gestor de Archivos - (Modo Superusuario):


Al hacerlo, se nos pedirá la contraseña de root. La pondremos y pulsaremos el botón Aceptar:


Una vez que Dolphin ya esté abierto, tendremos que ir a la siguiente carpeta (esta es la que corresponde a openSuse 13.2):
/etc/X11/xorg.conf.d/
Bastará con escribirlo en la barra de direcciones y pulsar Enter, como vemos a continuación:


Una vez que estamos en la carpeta correcta, debemos crear un archivo llamado "50-rat5.conf". Para ello, haremos clic con el botón derecho del ratón en cualquier zona vacía de la carpeta y, en el menú Crear archivo, elegiremos Archivo de texto... tal como se ve en la imagen:


Se nos pedirá el nombre y, tal como he comentado antes, tendremos que escribir "50-rat5.conf" y pulsar Aceptar:


Una vez creado, debemos hacer clic sobre el archivo para editarlo:


Aquí vemos una imagen del editor de texto con la configuración que he utilizado:


Como todos sabemos, es mucho más práctico copiar y pegar... así que aquí tenéis el texto:
Section "InputClass"
    Identifier "Mad Catz R.A.T. 5"
    MatchProduct "Mad Catz Mad Catz R.A.T.5 Mouse"
    MatchDevicePath "/dev/input/event*"
    Option "Buttons" "21"
    Option "ButtonMapping" "1 2 3 4 5 0 0 8 9 7 6 10 0 0 0 0 0 0 0 0"
    Option "ZAxisMapping" "4 5 11 10"
    Option "AutoReleaseButtons" "13 14 15"
EndSection 
El 8 y el 9 son los botones para avanzar y retroceder página en el navegador... la primera vez había probado con 9 y 8 en vez de 8 y 9 y me habían quedado con la función al revés... pero para gustos.

Una vez guardado el archivo de texto, es necesario finalizar la sesión y volver a acceder y, con esto, ya funcionará correctamente vuestro Mad Catz R.A.T. 5.

Espero que os haya sido de ayuda.

Un saludo: Roi

---

P.D.: Este mismo método también funciona en openSuse Leap 42.1 

3 de noviembre de 2015

Forzar uso de HTML5 en la reproducción de Youtube en Firefox

Hace ya tiempo que los navegadores más conocidos bloquean por defecto el plugin de Flash. Estos navegadores, son compatibles con HTML5 y resulta bastante molesto tener que permitir la ejecución de Flash cada vez que queremos reproducir un vídeo de Youtube (ya sé que podría elegir "permitir siempre", pero hay que ir olvidándose de Flash).

Aunque siempre se me reproducían los vídeos de Youtube sin hacer nada en Firefox, por algún motivo han vuelto a ejecutarse con Flash, probablemente por alguna actualización del navegador.

Si ese es vuestro caso, al acceder a algún vídeo, se os pedirá activar el plugin de Flash:


Para indicarle a Youtube que lo que queremos es que reproduzca el vídeo utlizando HTML5 en vez de Flash, debemos acceder a la página https://www.youtube.com/html5. En ella, veremos el botón "Solicitar el reproductor HTML5". Simplemente, debemos pulsarlo:


Una vez pulsado, ya podemos volver a nuestro vídeo y reproducirlo sin problemas:


Si por el motivo que sea, preferimos volver a utilizar Flash, sólo tendremos que acceder de nuevo a la web https://www.youtube.com/html5 y pulsar sobre el botón "Utilizar el reproductor predeterminado":


Siempre que podamos evitarlo, es recomendable no utilizar el plugin de Flash

Un saludo: Roi.

13 de marzo de 2015

Ver IP pública desde un terminal en Linux

Teniendo curl instalado en cualquier distribución, para ver nuestra IP pública basta con teclear lo siguiente en un terminal:

  curl ifconfig.me

Podemos verlo en ejecución en la siguiente imagen:


Evidentemente, donde pone "ip.pub.li.ca", aparecerá la IP que tenéis en ese momento.

1 de diciembre de 2014

Solucionar error "cache rpm" en openSuse 13.1

En medio de una actualización de mi openSuse 13.1, se fue la luz de pronto y, a partir de ese momento, cada vez que actualizo salta el siguiente error al terminar:

Failed to cache rpm database
Aunque se actualizan las aplicaciones correctamente, es un error algo molesto. Para solucionarlo, debemos abrir un terminal y, como root, ejecutar el siguiente comando:

rpm --rebuilddb
Tras unos segundos, quedará reparada la base de datos de los rpm y no debería repetirse el error.

Espero que os haya sido útil este pequeño post.

23 de noviembre de 2014

Cómo instalar Factusol 2015 en Ubuntu 14.04

Hace tiempo publiqué una entrada de cómo instalar Factusol en Opensuse 12.3. Hace poco, un amigo me ha comentado que, aún siguiendo los pasos, no logra instalarlo en Ubuntu 14.04 por recibir un error:

"Run-time error '5': Invalid procedure call or argument"

Pensando en cuál sería el problema, me he decidido a instalar Ubuntu 14.04 32bit en una unidad virtual y probar a instalarlo.

[Nota: para poder cambiar la resolución de pantalla de Ubuntu dentro de Virtualbox, llega con instalar las Guest Additions, reiniciar la máquina y ya aparecerá a 1024x768, en vez de 640x480]

Tened en cuenta que lo he instalado directamente con un Ubuntu recién instalado... hay algún momento en el que podía haber ahorrado pasos, pero lo he ido escribiendo tal como me iban surgiendo las instalaciones.

Veamos cómo lo he hecho:

Lo primero ha sido ir a la web del programa Factusol y pulsar el botón "Descargar Factusol":


Tras hacerlo, nos pedirá un correo electrónico para enviarnos el enlace. Lo escribimos, marcaremos la casilla "Acepto la política de privacidad" (tras leerla, evidentemente), y pulsaremos el botón "Enviar":


En un momento, recibiremos un correo similar al siguiente:


Simplemente, debemos hacer clic en el botón "Descargar Factusol" y guardar el instalador. Más tarde lo necesitaremos.

Ahora, debemos instalar wine en nuestro Ubuntu. Así que iremos al Centro de Software y lo buscaremos para instalarlo:


Tras pulsar "Instalar", se nos pedirá autenticarnos como administrador. Escribiremos la contraseña correspondiente y pulsaremos "Autenticar":


En mi caso, la versión estable de wine disponible en este momento ha sido la 1.6.2.

Antes de intentar instalar el Factusol, debemos añadir algunas librerías a nuestro wine. Para ello, pulsamos Alt+F2 y ejecutamos winetricks, simplemente escribiéndolo y pulsando sobre el icono de colores:


Al abrirse winetricks, debemos seleccionar "Select the default wineprefix" y pulsar el botón "Aceptar":


Como es la primera vez que lo ejecuto, se me pregunta si quiero enviar estadísticas de uso a los desarrolladores de winetricks. Dejo la elección a vuestro gusto. En mi caso he pulsado "Sí":


Se me ha mostrado después un mensaje de agradecimiento y he pulsado "Aceptar":


Se nos muestran ahora varias opciones. Elegimos la primera, ya que es la que nos permitirá agregar nuevas librerías a nuestro wine. Así que, seleccionamos "Install a Windows DLL or component" y pulsamos el botón "Aceptar":


En la nueva ventana, seleccionamos los siguientes componentes:
  • comctl32
  • jet40
  • mdac27
  • mdac28
  • mfc40
  • mfc42
  • vb6run
Una vez seleccionados, pulsamos "Aceptar":


 Se nos pide aceptar una de las licencias, pulsamos "Yes":


 Se nos pregunta por una licencia más, y también pulsamos "Yes":


Para los MDAC se nos pide que realicemos la descarga manualmente, con lo que se nos abre automáticamente la página correspondiente en el navegador. Pulsamos en "Download Now":



 Se nos pedirá guardar el archivo, así que pulsamos en "Save file":


Al finalizar la descarga, vemos un mensaje de winetricks indicándonos que el archivo debe guardarse en una carpeta concreta (/home/nombredetuusuario/.cache/winetricks/mdac27) y que ya se nos ha abierto en el gestor de archivos:


Pulsamos el botón "Aceptar" en el mensaje de winetricks y dejamos la otra ventana abierta.

Aprovechamos ahora para abrir un nuevo gestor de archivos, ir a la carpeta de Descargas y así arrastrar el archivo a su lugar correcto. 

Para ello, primero abrimos una nueva ventana del gestor de archivos haciendo clic derecho sobre él y pulsando en "Abrir una ventana nueva":


Una vez abierta, iremos a la carpeta de "Descargas" y arrastraremos el archivo recién descargado a la otra ventana:


Una vez hecho, ya queda colocado el archivo en su sitio y podemos cerrar las dos ventanas:


Debemos ahora ejecutar de nuevo winetricks tal como habíamos comentado antes y seleccionar de nuevo las mismas opciones y librerías. En resumen:

  1. Alt+F2
  2. Escribimos winetricks y lo abrimos
  3. Seleccionamos "Select the default wineprefix"
  4. Pulsamos "Aceptar"
  5. Seleccionamos "Install a Windows DLL or component"
  6. Pulsamos "Aceptar"
Ya estará marcada la primera de las librerías (comctl32), marcamos las demás:
  • jet40
  • mdac27
  • mdac28
  • mfc40
  • mfc42
  • vb6run
Y de nuevo pulsamos "Aceptar":


 Se nos pide aceptar una nueva licencia, marcamos la casilla y pulsamos "Siguiente":


Tras hacer varias operaciones, se indica que se instalarán los componentes y que pulse "Finalizar":


Una vez terminada la instalación, pulsaremos "Close":


Ahora le toca a la licencia del Jet, pulsaremos "Yes":


Tras unos segundos, nos salta la licencia del MDAC28, marcaremos la casilla y pulsaremos "Siguiente":


Al igual que en el MDAC27, ahora se nos pide pulsar "Finalizar" para comenzar la instalación. Hacemos clic en "Finalizar":


Tras instalarse, pulsaremos el botón "Close":


Nos toca ahora aceptar una licencia en alemán. Pulsaremos el botón "Ja":


Y una licencia más, la de las librerías en tiempo de ejecución de VB6. Pulsaremos "Yes":


Como winetricks ha terminado con las instalaciones, vuelve a preguntarnos qué deseamos hacer. Simplemente cerraremos la ventana:


Se abrirá la primera ventana de wineprefix, y la cerramos también:

Ahora vamos a instalar el Factusol. Abriremos un terminal pulsando Ctrl+Alt+T y accederemos a la carpeta de Descargas con el comando "cd Descargas":


Ejecutaremos el instalador con "msiexec /i factusolinstalarweb.msi":


Veremos ahora el instalador, pulsaremos "Siguiente":


Aceptamos la licencia y pulsamos "Siguiente" de nuevo:


Cubrimos los datos que se nos piden y pulsamos "Siguiente" una vez más:



Se nos solicita la carpeta donde instalarlo. Lo dejamos así y pulsamos "Siguiente":


"Siguiente" una vez más:


Esperamos mientras se instala:


Una vez instalado, pulsamos "Finalizar":


Se nos habrán creado dos iconos en el escritorio. Utilizaremos el segundo para abrir el Factusol:


Vemos que el Factusol se ejecuta correctamente:


Faltaría crear una empresa y esas cosas... pero ya podemos empezar a utilizarlo:


Y hasta aquí el pasito a pasito de la instalación de Factusol 2015 en Ubuntu 14.04 32bit. Espero que os haya sido de ayuda.

Un saludo: Roi

----

+Billington Estrada indica que, a mayores, para hacerlo funcionar ha necesitado ejecutar dos comandos más desde un terminal:

winetricks native_mdac
winetricks native_oleaut32

Yo no los he necesitado, pero si tenéis algún problema, seguramente os sirvan de ayuda.

Gracias por comentarlo +Billington Estrada.