martes, 19 de abril de 2016

Evitando obstáculos (1)

A título de experimentar un poco con el sensor de ultrasonido, realizo esta entrada, comentando mi primera experiencia con este dispositivo.

Se trata de aplicar (y adaptar) los programas Arduino clásicos para el uso de este tipo de sensor.

En primer lugar, con una placa metaboard el monitor serial del entorno de programación no funciona, esto se debe al hecho de tener que usar el driver USBasp en la comunicación entorno-metaboard, al no existir puerto relacionado, sencillamente no funciona el mentado monitor.

Pero no es desesperante gracias a las bondades de Android y lo sencillo que es enviar o recibir información usando MIT App inventor.

Realizar un programa para leer solo un dato (la distancia medida con el sensor) es tan sencillo que hasta da vergüenza publicarlo... pero aquí está:
Muy pocos objetos como se puede ver, tal vez lo más importante sea el temporizador (Reloj1) que se encarga de enviar una consulta al metaboard para que éste responda con la información solicitada.

Los bloques que hacen al programa, al arrancar la aplicación se hace visible el componente SelectorDeLista1, invisible el Botón1 (el botón de desconectar) y se "apaga" el temporizador, cómo estas acciones -ya sea en ese orden o al revés- se realizan varias veces, es conveniente hacerlas en un procedimiento o subrutina (llamado "inicio").

Luego, al pulsar el objeto SelectorDeLista1 (AntesDeSelección) se abre la lista de dispositivos BlueTooth habilitados y reconocidos en nuestro dispositivo Android, al seleccionar el que se corresponda con el metaboard (en mi caso, se llama "linvor") se ejecuta la acción DespuesDeSelección donde se conecta con el dispositivo seleccionado, y si la conexión tiene éxito se invoca al procedimiento Inicio para hacer lo contrario que en la primer invocación, o sea invisible el SelectorDeLista1 (Conectar), visible el Botón1 (Desconectar) y habilitar el temporizador (Reloj1), para que éste comience a hacer su trabajo periódicamente.

El temporizador periódicamente (está seteado en 100 ms) se encarga de enviar el número 222 (nada, solo me gustaron los tres patitos) cómo "pedido de consulta" y recibir el valor de la distancia medida en formato texto para asignar dicha información a la Etiqueta2 en su propiedad Texto.

¿Porqué hacer un pedido de consulta si en realidad solo basta leer el dato? Opté por esta técnica para no usar retardos de tiempos en el metaboard, de esta forma simplifico la coordinación de tiempos entre ambos programas, desentendiéndome así de la correspondiente sincronización entre ambos dispositivos.

Por último, el botón1 se encarga de desconectar la conexión BlueTooth, así como dejar los objetos listos para una mueva conexión, y limpiar el texto de la etiqueta2.


Para el ATMEGA desarrollé dos programas, ambos basados en los ejemplos de las páginas recomendadas en la entrada anterior.

El primer programa utiliza la librería NewPing, una librería desarrollada para sensores de este tipo, creada por Tim Eckel, que brinda las siguientes funciones:
  • Sonar.ping() - Enviar un ping y obtener el tiempo de eco (en microsegundos) como resultado.
  • Sonar.ping_in() - Enviar un ping y obtener la distancia en pulgadas enteras.
  • Sonar.ping_cm() - Enviar un ping y obtener la distancia en centímetros enteros.
  • sonar.ping_median(iteraciones) - Varios pings (por defecto 5), devuelve el promedio en us.
  • Sonar.convert_in(Echotime) -  Convertir tiempo de eco de microsegundos a pulgadas.
  • Sonar.convert cm(Echotime) - Convertir tiempo de eco de microsegundos a centímetros.
  • Sonar.ping_timer(función) - Enviar un ping y esperar que se complete la función.
  • Sonar.check_timer() - Comprueba el regreso del ping dentro del límite de distancia establecida.
  • NewPing::timer_us(frecuencia, función) - Llama a una función  a frecuencia dada en us.
  • NewPing::timer_ms(frecuencia, la función) - Llama a una función  a frecuencia dada en ms.
  • NewPing::timer_stop() - Parar el temporizador.
Una librería muy completa por cierto, de estas funciones solo utilizo sonar.ping_cm(), cómo se ve en el siguiente código:

//Librerías a incluir
#include <NewSoftSerial.h>
#include <NewPing.h>

//Definición de parámetros para el objeto "sonar"
#define TRIGGER_PIN  4 
#define ECHO_PIN     5 
#define MAX_DISTANCE 100 

//Declaración de pines para el objeto "Blue"
const int RX =  9;     
const int TX = 10;

//Variable donde se recoge la distancia medida
unsigned long distancia = 0;

//Creación de los objetos
NewSoftSerial Blue(RX, TX); 
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);

//Configuración y seteo de velocidad de comunicación
void setup() {
  pinMode(RX, INPUT );
  pinMode(TX, OUTPUT);
  Blue.begin(9600);
}

//Función principal
void loop() {
  if (Blue.available() > 0) { 
    if (Blue.read() == 222) { 
      distancia = sonar.ping_cm(); 

      if (distancia <= 25)         
        Blue.print(" " + String(distancia)); 
      else  
        Blue.print(" ¡muchos!"); 
    }
  }
}

En cuanto a las zonas de inclusión, definiciones, declaraciones, variables, creación de objetos y configuración, no hay nada que decir, salvo que usé los pines 4 (activar el inicio de una medición) y 5 (pin donde se hace eco del tiempo de medición para calcular la distancia) porque son los que tenía libres.

El algoritmo del cuerpo principal del programa es el siguiente:

  SI (HAY DATOS EN BUFER DEL OBJETO BLUETOOTH) ENTONCES
    SI (EL DATO LEÍDO ES 222) ENTONCES
      LEER LA DISTANCIA EN CM
      SI (LA DISTANCIA ES MENOR O IGUAL A 25 cm) ENTONCES
        INFORMAR LA DISTANCIA MEDIDA
      SINO
        INFORMAR SOBRE PASO DEL LÍMITE
      FIN SI
    FIN SI
  FIN SI

Para decirlo simplemente: si el dispositivo Android solicita el dato de distancia, se lo informamos siempre y cuando esté dentro de los 25 centímetros, de lo contrario informamos de que la distancia es mayor a dicho límite.

Es de notar que el objeto "sonar" ya dispone de un límite (MAX_DISTANCE 100) a 100 centímetros, que, según sea la calidad del módulo de ultrasonidos disponible, puede variar ente 400 o 500 centímetros máximo, pasado ese límite devuelve el valor 0. Mi límite en 25 cm es puramente superfluo.

Cómo curiosidad, al momento de convertir el dato de distancia a una cadena, debo incorporar por delante de la cadena un espacio; supongamos una distancia medida de 15, se envía la cadena " 15", y se recibe en Android la cadena "15 ", con el espacio al final (no me pregunten por qué).

Como puede verse, el programa es extremadamente sencillo y se lo puede simplificar más aún, con lo que cabe preguntarse si es necesario usar esta librería que nos brinda tantas funciones si solo vamos a usar una apenas de todas.


Como segundo programa experimental, evito usar la librería NewPing, con lo que hay que decirle entonces al sensor cuando comenzar a medir el tiempo de rebote para poder calcular la la distancia.

El programa completo es el siguiente, también desarrollado a partir de los ejemplos disponibles en Internet:

//Librerías a incluir
#include <NewSoftSerial.h>

//Declaración de pines
const int TIGGER =  4;
const int ECHO   =  5;
const int RX     =  9; 
const int TX     = 10;

//Variables para distancia y el tiempo de medición
unsigned long distancia = 0;
unsigned long tiempo    = 0;

//Creación de los objetos
NewSoftSerial Blue(RX, TX); 

//Configuración y seteo de velocidad de comunicación
void setup() {
  pinMode(TIGGER, OUTPUT); 
  pinMode(ECHO,   INPUT ); 
  pinMode(RX,     INPUT );
  pinMode(TX,     OUTPUT);
  Blue.begin(9600);
}
//Función principal
void loop() {
  if(Blue.available() > 0) {
    if (Blue.read() == 222) {
      
      digitalWrite(TIGGER, LOW); 
      delayMicroseconds(2);
      digitalWrite(TIGGER, HIGH); 
      delayMicroseconds(10);
      digitalWrite(TIGGER, LOW); 
  
      tiempo    = pulseIn(ECHO, HIGH); 
      distancia = int(0.017 * tiempo);
      
      if (distancia <= 25)
        Blue.print(" " + String(distancia));
      else  
        Blue.print(" ¡muchos!");
    }
  }
}

En la función loop(), una vez detectado la solicitud desde Android, lo que se hace es bien sencillo:

      digitalWrite(TIGGER, LOW); 
      delayMicroseconds(2);
      digitalWrite(TIGGER, HIGH); 
      delayMicroseconds(10);
      digitalWrite(TIGGER, LOW); 

Estas líneas generan un pulso en el pin TIGGER, de la siguiente forma:

1) se mantiene TIGGER en nivel bajo (0 volts) durante 2 us, para estabilizar al sensor.
2) iniciamos el pulso pasando TIGGER a nivel alto (5 volts) durante 10 microsegundos.
3) y terminamos el pulso volviendo TIGGER en nivel bajo (0 volts).

Esta es la forma de decirle al sensor que comience con una medición de tiempo, a partir de este pulso el sensor enviará un tren de ocho pulsos ultrasónicos por el "parlante" (perdón por semejante comparación) y los recibirá en el "micrófono" (de nuevo, perdón...) si es que hay rebote en algún objeto, de lo contrario no recibirá nada (imagino un murciélago, o un radar).

    tiempo = pulseIn(ECHO, HIGH);

La función PulseIn() mide la longitud del pulso entrante. O sea el tiempo transcurrido entre el envío del pulso ultrasónico y el momento en el que el sensor recibe el rebote, es decir: desde que en el pin ECHO sube a HIGH, hasta que baja a LOW, midiendo entonces la longitud del pulso entrante, si el tiempo en que se encuentra en HIGH es mayor que 38 ms, se interpreta que no hay obstáculo, si se encuentra dentro del rango de 150 us a 25 ms, entonces hay un obstáculo dentro del rango de medición. Una imagen vale por mil palabras.


Tenemos entonces el tiempo transcurrido, que será mayor cuanto más lejos se encuentre el objeto del sensor, vale aclarar que se trata del tiempo de ida hacia el objeto y el tiempo de retorno desde el objeto.

Queda calcular la distancia, que, en base a la fórmula, se simplifica a lo siguiente:

      distancia = int(0.017 * tiempo);

estás dos líneas de programación podrían simplificarse a:

      distancia = int(0.017 * pulseIn(ECHO, HIGH));

 Evitando así la variable Tiempo.

En este ejemplo, se obtienen distancias en cm enteros, dado que se trata de probar al sensor, no de hacer mediciones exactas.

Las últimas líneas del programa se encargan de enviar el resultado de la misma forma que en el programa anterior.


---------------------------------------------------------------------------------------------------------------

No hay comentarios.:

Publicar un comentario