lunes, 12 de octubre de 2015

Cálculo de distancias por ultrasonidos

Cálculo de distancias por ultrasonidos


Hola de nuevo. Vamos a ver en esta entrada cómo calcular distancias con un Arduino y un sensor de ultrasonidos tipo el HC-SR04, o similar. Este tipo de montajes tiene diferentes utilidades, pero el denominador común al calculo de distancias.

Yo lo utilizo para evitar las colisiones en los robots domésticos, o bajar velocidades en otras aplicaciones cuando me aproximo al sujeto.  En cualquier caso la precisión decae cuando el obstáculo no está perfectamente perpendicular al sensor, por lo que se debe mejorar el sistema, en caso de aplicaciones críticas.

Pero veamos primero en que basa su funcionamiento este sistema.

Este sensor, usa dos transductores, un emisor y un receptor que trabajan en la frecuencia de los ultrasonidos, inaudible para el ser humano. El emisor emite un pulso que rebota en el obstáculo, permitiendo que el receptor capte el eco de ese pulso. Gráficamente sería algo como esto:



Necesitaremos un poquito de mates y física básica, para poder modelar este comportamiento con nuestro Arduino. Nos interesa conocer la distancia del objeto, por lo que emplearemos la fórmula que relaciona las variables, velocidad, espacio (distancia) y tiempo.

            v=d/t

Despejando la distancia que, que es lo que nos interesa, obtenemos que :

            d=v·t


La velocidad del sonido la conocemos, en el aire a 20ºC es de 343 m/s.

El tiempo, es lo que calcularemos con nuestro Arduino. ¿Cómo? Calculando el tiempo que pasa entre que el traductor emite el sonido, y el receptor lo capta. Para eso necesitaremos presentar una función nueva, pulseIn

Esta funcion sirve para medir pulsos, es decir, el intervalo que pasa entre que una señal pasa de un nivel bajo a un nivel alto o viceversa. La medición la realiza en microsegundos.

La sintaxis de esta función es:


pulseIn(pin, value, timeout)


Donde:
  • pin, es el conector donde conectaremos el receptor de la señal, v
  • value, que debe ser HIGH o LOW, según si queremos detectar un flanco de subida o de bajada, para el caso que nos ocupa, HIGH.
  • timeput, es opcional, y sirve para delimitar el tiempo que queremos que nuestro Arduino esté "escuchando" en espera de un cambio. Si el tiempo de espera supera los 3 minutos dará un error de timeout ( devuelve un 0).
Este valor, es más importante de lo que parece, pues es el que nos dará fluidez en el programa. Por un lado limitará el tiempo de escucha agilizándolo. También es razonable que si el sensor no puede medir más de 6 metros , la escucha sea de 3 minutos, es decir detectar un objeto a más de                ¡¡¡30 kilómetros !!!


Por último, es importante tener en cuenta que las ondas realizan un trayecto de ida y vuelta. Es decir, se emiten, van al objeto, rebotan y vuelven. ¿ Lo has adivinado? Pues sí, la distancia que calculamos, la tenemos que vivir entre dos para obtener lo que andábamos buscando.

Bueno, después de la introducción teórica, vamos a ver el montaje, que en esta ocasión , es bien sencillo:



Y el sketch, que quedaría de la siguiente manera. 

long distancia; 
long tiempo;
int trigger=9;// pin trigger del SR04
int echo=8;// pin echo del SR04

void setup (){
  Serial.begin(9600);// preparamos el port serial para ver lecturas
  pinMode(trigger,OUTPUT); // definimos trigger  como salida
  pinMode(echo,INPUT); // y echo como entrada
}

void loop (){
  digitalWrite(trigger,LOW);  // Aseguramos que esta 0
  delayMicroseconds(5);       //Retraso para asegurar respuesta
  digitalWrite(trigger,HIGH); // Lanzamos el pulso
  delayMicroseconds(10);//
  tiempo=pulseIn(echo,HIGH,2945);  // MEdimos el tiempo del rebote
  // timeout calculado para 50cm
  distancia = (0.0343*(tiempo/2));//Usamos las unidades apropiadas, en 
  // este casola velocidad del sonido en cm/microsegundos
  // Y lo dividimos entre 2 para mostrar distancia "solo ida"
  Serial.println("Distancia");//mostramos resultados 
  Serial.println(distancia);  //en el monitor serial
  Serial.print("cm"); // en cm
  
}


Quedaría explicar  el valor del timeout.

Imaginad que quereis ignorar, distancias superiores a 50cm. Por tanto deberemos calcular que tardaría la onda de sonido en recorrer 50cm, ida y vuelta. Despejando el tiempo de la fórmula anterior:


                                                                        t=e/v

Usando las unidades apropiadas, cm y cm/us, (recordar multiplicar la distancia x 2 )nos daría un tiempo de,

                t=e/v= 100/0,0343 = 2915 microsegundos

Haced vosotros la cuentas de la cantidad de cosas que puede hacer nuestro Arduino con el tiempo que    se ahorra limitando el tiempo de escucha. Borrando el valor, el limitante será la capacidad del sensor que usemos.


Y como función?

Como ya explicamos en una entrada anterior, ésta, sería interesante generar una función para que fuese más sencillo introducirlo en un programa más complejo, como por ejemplo un robot de suelo, o similar, de manera que se pudiese llamar en cualquier momento. Esta parte os la dejo a vosotros, pero no dudes en plantear cualquier duda.

Nos vemos!

Os dejo con un video del experimento. Como vereis la precisión es mejorable, pero es un buen mecanismo de aproximación y anticolisiones.








lunes, 29 de junio de 2015

Arduino , Ethernet Shield y algo de HTML

Repasando la entrada anterior, me gustaría comentar algunos puntos que no se tocaron y que pueden resultar de interés.
El primero tiene que ver con el hardware del ejemplo. Hay mucha variedad de Ethernet Shields en el mercado. Os recomendé un par que son compatibles con el Arduino, hay muchas más, pero debéis fijaros que el CHIP que use sea el W5100 Ethernet controller IC , para evitar problemas de compatibilidad.

 El siguiente punto, también de hardware tiene que ver con el consumo de ese chip. Puede llegar a ser muy alto, y además de calentarse, y de qué manera, es necesario alimentar el ejemplo con una fuente de alimentación externa, pues vuestra conexión USB no dispone de la potencia necesaria. Si aún no lo habíais hecho, es un buen momento para hacerse con una fuente externa de 9 V para alimentar vuestro Arduino.
El último punto relacionado con el hardware,  tiene que ver con los pins 10,11,12 y 13 del Arduino. Estos pins se usan para la comunicación con la Ethenet shield, por lo que no podrán ser usados para otro fin cuando se use con esta ampliación.
El resto de puntos que me gustaría tratar, están relacionados con el código del sketch de ejemplo. Al final lo que conseguimos es convertir nuestro Arduino en un servidor web.  La página mostrada es de una sencillez máxima,(es un ejemplo “demo”)  y tampoco veréis grandes florituras en los ejemplos míos  que vendrán, pues mis conocimientos de HTML son básicos.
Pero me gustaría desarrollar un poco las líneas más básicas de código HTML para mostrar la información de una manera mínimamente inteligible.
Hasta ahora, en algún ejemplo como este, habíamos usado la instrucción serial.print(); para mostrar la información que queríamos en el Monitor Serial.
Sin embargo en este ejemplo usamos la instrucción :

client.println();

Entre los parantésis podemos escribir desde un texto entre comillas:

            client.print("analog input ");


O el valor de una variable cualquiera:

            client.print(sensorReading);


Salto de línea, mostrará el siguiente valor o texto en otra línea:

            client.println("<br />");      


Y dos líneas imprescindibles que marcan el inicio y el final de un código en HTML:

            client.println("<html>");


Y
          client.println("</html>");


Otra instrucción también interesante, es la que controla el tiempo de refresco de la página. En el ejemplo del día anterior era cada 5 segundos:

client.println("Refresh: 5"); // refresh the page automatically every 5 sec

Eso es todo de momento. Espero hayáis podido configurar vuestra Ethernet Shield y el router sin problemas, pues seguiremos avanzando en las próximas entradas en estos temas.

viernes, 12 de junio de 2015

Conectando nuestro Arduino a Internet

Es momento de abrir al mundo nuestro Arduino. Vamos a configurar todo lo necesario para conectar a internet nuestro Arduino através de una shield Ethernet.

Este post veremos cómo configurar nuestro entorno de trabajo para poder sacar partido de esta shield, que creo que es de las más productivas que podemos encontrar. Veremos cómo configurar el router, como parametrizar la shield Ethernet y un sencillo ejemplo que puede servir de base para desarrollar otros proyectos.

Los requisitos necesarios para poder sacar partido de este ejemplo son:

-       - Placa Arduino UNO, o compatible, por poco más de 6€
-       - Placa shield Ethernet  compatible, un coste similar de unos 6 €
-       - La librería Ethernet, http://arduino.cc/en/Reference/Ethernet
-       - Sería recomendable, tener nociones de HTML,
-       - Un router conectado a internet, y sus instrucciones si es posible
-       - Cable RJ-45
-       - Paciencia y algo para hidratarnos que ya empieza a hacer demasiada calor
-       - Opcionalmente, ya  lo explicaremos, un IP fija o servicio DDNS equivalente

Vamos a ello,

El router


Antes de seguir, debo advertir que este ejemplo está realizado con mi router. Se trata del que facilita  mi proveedor de internet (no lo nombraré hasta que no me paguen por ello ),  y hay que advertir que cada modelo gestiona las opciones de configuración de manera distinta. La parte positiva, es que de todas maneras las opciones suelen ser bastante similares y no difieren mucho de un modelo a otro.
En cualquier caso, podéis localizar mucha información sobre configuración de routers en páginas como esta www.adslayuda.com

¿Preparados? Bien, pues lo primero es conocer en que dirección IP podemos configurar nuestro router. Podemos mirar si físicamente tiene alguna pegatina, (versión cutre pero efectiva), o lanzar unos comandos desde nuestro ordenador.

Si utilizas Windows, los pasos son estos:
 -  Botón de inicio y busca ejecutar
-      -  Teclea en la pantalla cmd y enter ( Estos dos pasos son equivalentes a pulsar Tecla windows + R)
-      -  Tendrás ahora una ventana con fondo negro del tipo MS-DOS, teclea ipconfig y enter

-        En la pantalla que te habrá salido, busca la línea Puerta de enlace predeterminada, esa es tu dirección IP.




 Ahora con tu dirección IP, abre una pestaña de tu navegador favorito y pega el número tal cual en la barra de direcciones. 

Según el ejemplo : http://192.168.1.1

Te saldrá la pantalla de bienvenida de tu router, y los más probable, dos espacios solicitando usuario y contraseña.



Si no los sabes, puedes buscar con google, por tu modelo y/o operador de internet. Este sitio te puede servir de referencia. Los más habituales:

-        Usario:  admin, user,(en blanco)
-        Contraseña: admin, user, 1234,(en blanco)

Llegados a este punto, deberíamos haber sido capaces de acceder al entorno de configuración del router.  El siguiente paso es :

Asignar una IP al Arduino


Ahora, necesitamos que cada vez que nuestro Arduino se conecte a nuestra red local, lo haga siempre a la misma dirección. Si no lo configuramos, el router va gestionando las direcciónes IP de manera automática y toalmente aleatoria, y nosotros necesitamos que sea siempre la misma.

¿Cómo? Primero asignaremos a nuestra shield un identificador único. Esto lo hacemos desde el void setup, configurando el MAC de la placa. Podéis profundizar más sobre el tema aquí, pero básicamente se trata de un identificador con la dirección física del dispositivo, lo tiene cualquier aparato que se conecte a la red, y la única premisa que debe cumplir es que no sea igual al de alguno de los dispositivos conectado en nuestra red. Por defecto, puedes usar :

0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED

Y la instrucción completa:

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

Si nuestro proyecto tuviese dos shields Ethernet este número debería ser diferente para cada una de ellas.

Podéis comprobar las direcciones MAC de los equipo conectados buscando entre las opciones del router algo del tipo Device Info - > DCHP, donde os saldrá una tabla con las direcciones MAC de los dispositivos conectados la última semana.

Ahora es momento de localizar en el menú del router dentro de las opciones de DCHP, algo  parecido a DCHP estáticas, o asignaciones de MAC . Es el punto donde debemos escoger la dirección IP que asignaremos al Arduino y decirle al router la MAC que corresponde al Arduino. Una imagen de como queda en mi router.



Accediendo desde el “exterior”



Una vez completados los pasos anteriores ya puedes acceder vía IP desde tu red local al Arduino, y probar el ejemplo que hay más adelante. Tan solo tendrías que teclear la dirección IP que le hemos asignado dentro de la barra del navegador. Algo como esto http://192.168.1.177

Para poder acceder desde fuera de la red local, tenemos que decirle al router que permita las conexiones entrantes. Para ello habilitaremos un puerto, el nº 80 por ejemplo para permitirlo. A muchos les sonará esto del emule... para los que no deberemos buscar en el router un menú relacionado con puertos, NAT, virtual servers o similar. En caso contrario, una búsqueda en google con el nombre de vuestro equipo del estilo : abrir puertos router XXXXX, os solventará el problema.
 Recordad , puerto 80, y la IP que le hemos asignado al Arduino la 192.168.1.177 .






¿Esto es todo? No, I'm sorry. Falta saber la dirección IP que el proveedor nos asigna a nuestra conexión. Para eso teclear en vuestro navegador:

www.cualesmiip.com  o  www.sabermiip.es


Anotad la dirección que os aparece, cerrad el navegador y reiniciar el router. Ahora volved a teclearlo y comprobad si los números son iguales. No coincide ¿verdad? , es normal que no coincidan, eso significa que tu IP es dinámica, como la del 99% de los usuarios.
¿Que implica que tu IP sea dinámica? El número que te ha mostrado es tu dirección IP, tecleando desde cualquier parte del mundo accederías a tu red, o al menos al puerto 80 ( es el que hemos abierto).  Si es dinámica, cambiará ante cualquier incidencia ( corte de tensión o reasignación por parte del proveedor), y te impedirá acceder a tu sistema.

Tranquilo, esto tiene solución, no hemos llegado hasta aquí para abandonar ahora. Tienes al menos dos maneras para resolverlo:

1 .- Contratar con tu suministrador un servicio de IP estática o fija. El coste ronda los 30€ anuales. El inconveniente, al margen del precio, es que una dirección tan solo numérica es difícil de recordar.

2.- Contratar un servicio de DDNS. Los hay de pago o gratuitos, y lo que hacen es seguir tu IP dinámica y asignarla a un nombre de servidor ( de tu elección) que lo hace más fácil de recordar. El inconveniente es que tu router debe soportar este servicio. Algunos ejemplos de proveedores de este servicio pueden ser, DynDNS, TZO, NoIP (gratuito con reactivación mensual), DNSdynamic (gratuito). Configurarlo en tu router será tan sencillo como buscar la opción e introducir el usuario y contraseña asignados. Recuerda que el registro previo se ha decir vía web.



Recuerda que no es necesario, ni abrir puertos, ni configurar el DDNS para poder realizar el ejercicio propuesto. Esto sólo será necesario en caso de querer hacerlo funcionar desde fuera de nuestra red local.

Una forma rápida de comprobar si todo gira, uno de los ejemplos de los que vienen por defecto en el IDE de Arduino. Os recomiendo:

Archivo -> Ejemplos -> Ethernet -> WebClient

Abrir el navegador y poner la dirección 192.168.1.177 si estás conectado en tu red local (lo recomiendo para empezar), o tu dirección IP. Obtendrás una pantalla con 6 lecturas analógicas totalmente arbitrarias, pues no hay nada conectado.



Hay trabajo para rato con lo aquí propuesto para configurar vuestra red local, así que os dejo en este punto. El montaje consistiría únicamente en montar la shield encima del Arduino, alimentar el conjunto y conectar a la red local mediante un cable RJ-45. Poned los valores propuestos, pues son los que usa el ejemplo del IDE.

En la siguiente entrada habilitaremos el código para leer un TMP-36 y hacer una lectura real de temperatura. Nos vemos!

viernes, 22 de mayo de 2015

Termómetro diario ( II )

Sigamos con el ejemplo del día anterior. Después de completar en casa el ejemplo, el código tenía una complejidad considerable. Bueno, para ser más exactos, más que complejo empezaba a tener unas dimensiones considerables, y la programación tal y como la hemos visto hasta ahora, hacía muy complicado e ininteligible el código del programa.

Por lo que he decido hacer un bloque principal para el cuerpo del programa, y diferentes "subprogramas" que realizarán los cálculos necesarios , o las acciones complementarias que iremos describiendo.

Estos "subprogramas" reciben el nombre de funciones.

Uso de funciones con Arduino

Aunque pueda parecer algo nuevo, ya hemos usado funciones en nuestros sketches de Arduino, son las funciones void setup y void loop, que además son de uso obligatorio.

Las funciones personalizadas se han de declarar siempre después del void loop. A partir de aquí existen diferentes formas de trabajar según si la función debe devolver valores ( el resultado de una operación), o simplemente ejecutar una parte del código.

Mejor un ejemplo:


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

void loop()
{
  int a = 2;
  int b = 3;
  int c;

  c = FuncionMultiplicarNumeros(a, b); // C vale 6
  Serial.println(c);
  delay(500);
}

int FuncionMultiplicarNumeros(int x, int y) 
{
  int resultado;
  resultado = x * y;
  return resultado;
}







En este ejemplo, declaramos una función del tipo integer. Le pasamos dos valores a y b  , que queremos que multiplique. Para ello, dentro de la función definimos dos variables más, las variables x  e Y, que usaremos para realizar los cálculos, y que solo serán validos dentro de la función de multiplicar.
Una vez realizada la operación devolvemos el resultado con la función return

¿ Se entiende? No verdad, yo tampoco lo entendí a la primera, por lo que a veces es mejor coger lápiz y papel y dibujar el baile de variables. 

Algunas de las FAQ's que comúnmente surgen tras el ejemplo es, ¿para qué tantas variables para multiplicar dos números? Bien, si solo tengo que multiplicar dos números, hasta el uso de funciones es un poco exagerado, pero definir las variables dentro de la función me permite "mandarle" cualquier pareja de valores en cualquier punto del programa.
En codigos más complejos también sirve para tener un programa más estructurado y nos permite ir programando bloques. Incluso, si trabajaremos en equipo para miembro podría hacerse cargo de programar una función concreta.

Un uso más sencillo, usar las variables definidas en el cuerpo del programa

Algo así:







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

void loop()
{
  int a = 2;
  int b = 3;
  int c;

  c = FuncionMultiplicarNumeros(); // C vale 6
  Serial.println(c);
  delay(500);
}

int FuncionMultiplicarNumeros() 
{
 c = a * b;;
}


Obtendría el mismo resultado, pues usaría las variables que se han declarado de manera global, pero solo me permitiría multiplicar los valores a y b. De la primera manera, insisto me permitirá obtener el resultado de diferentes valores.

Aprovechad, para tomar un respiro, ir al baño o beber algo de líquido antes de seguir.

De vuelta ya, os voy a colocar el código completo del ejercicio. Tomaros un tiempo en ver que hace cada función y entender porque se llaman en cada momento.

También se podría hacer una función para leer las pulsaciones de los botones, pero eso lo dejo en vuestras manos.

Para que en cualquier momento me pueda ofrecer una temperatura media, mínima o máxima coherente, antes de nada, en el void setup, inicializo TODOS los valores de la matriz con la primera muestra de temperatura. Es sólo un criterio, y cualquier otro podría ser válido. No olvides cambiar en el bucle milllis el 1000 por 360000 para obtener las muestras cada hora.




#include <LiquidCrystal.h>

LiquidCrystal lcd(4, 5, 6, 7, 8, 9);

float tempactual = 0; //mostrara la temperatura actual
float tempmin = 0; // VAlor minimo obtenido
float tempmax = 0; // el maximo
float tempmedia = 0; // y el minimo
float sensor = 0; 
float voltaje = 0;

int boton1 = 2; // Pulsador de Reset en el pin 2
int boton2 = 3; // El de set o show en el pin 3
int pulsacion = 0; // Necesaria para ller estado del pulsador
int accion = 0; // Truco para mantenerme en el bucle de la pulsacion
int posicion = 0; // Indicador o puntero de la matriz de Temperatura
float temperatura; // Para almacenar temperatura
float tempdata [24]; //Array para almacenar las muestras
unsigned long tiempoentremuestras = 0; // necesarias para los intervalos de
unsigned long tiempomuestraanterior = 0;// tiempo

void setup() {

  Serial.begin(9600);
  lcd.begin(16, 2); // Dimensiones de la pantalla
  lcd.setCursor(0, 0);
  lcd.print("    Arduino    ");//Para mi autoestima
  lcd.setCursor(0, 1);
  lcd.print("*   Practico   *"); // Pero lo puedes cambiar ;)
  delay(3000);
  lcd.clear();
  pinMode (boton1, INPUT);
  pinMode (boton2, INPUT);
  Calculo_temperatura(); // Medimos la temperatura
  Reset_Temp(); // E iniciamos las variables con la primera muestra
  
}

void loop()

{

  tiempomuestraanterior = millis();
// PAra muestras horarias sustituir el 1000 por 360000

// Cogemos muestras y vamos rellenado el array con los valores

  if ( tiempomuestraanterior - tiempoentremuestras > 1000)
  {

    Calculo_temperatura ();
    tempdata[posicion] = temperatura;
    posicion = posicion + 1;
    tiempoentremuestras = tiempomuestraanterior;

  }

  if (posicion > 23)
  {
    posicion = 0;
  }
  
  // Mostramos en pantalla la temperatura actual
  
  show_temperatura();

  // Empezamos el bloque para leer los pulsadores
  
  pulsacion = digitalRead(boton2);

// SI pulsamos mostramos las ultimas 24 muestras
// y los valores calculados

  if ( pulsacion == HIGH)

  {
    pulsacion = 0;
    Calculo_Tmax_Tmin_Tmedia();// llamada a la funcion de calculos
    lcd.clear(); // clear LCD screen
    lcd.setCursor(0, 0);
    lcd.print("Las ultimas 24");
    lcd.setCursor(0, 1);
    lcd.print("muestras son:");
    delay(1000);
    for (int j = 0; j <= 23; j++)
    {
      lcd.clear(); //
      lcd.setCursor(0, 0);
      lcd.print("Muestra: ");
      lcd.print(j + 1);
      lcd.print("/24  ");
      lcd.setCursor(0, 1);
      lcd.print(tempdata[j], 2);
      lcd.print(" grados C");
      delay(500);
    }
    lcd.clear(); // clear LCD screen
    lcd.setCursor(0, 0);
    lcd.print("Tmin  Tmed  TMax");
    lcd.setCursor(0,1);
    lcd.print(tempmin,1);
    lcd.setCursor(6, 1);
    lcd.print(tempmedia,1);
    lcd.setCursor(12, 1);
    lcd.print(tempmax,1);
    delay (3000);
    lcd.clear();
  }

// Lectura del pulsador de Reset

  pulsacion = digitalRead(boton1);
  if (pulsacion == HIGH)
  {
    pulsacion = 0;
    accion = 0;
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Reset min/max?");
    lcd.setCursor(0,1);
    lcd.print("1 = Yes, 2 = No");
    delay(500); // Tiempo para evitar rebotes
    while (accion == 0) // Aun no ha pasado nada
    {
      if (digitalRead(boton1)==HIGH)  // Ha pulsado si?
      {
        Reset_Temp();
        accion = 1;// Ha pasado algo y por tanto puedo salir del loop
        delay(500); // Tiempo para evitar rebotes
      }
      if (digitalRead(boton2)==HIGH) // Ha pulsado no?
      {
        accion = 1; // Ha pasado algo y por tanto puedo salir del loop
        delay(500); // Tiempo para evitar rebotes
      }

    } 
    lcd.clear();
  }




}

void Calculo_temperatura()

{

  sensor = analogRead(2);       // TMP36 sensor  pin 0
  voltaje = (sensor * 5000) / 1024;
  voltaje = voltaje - 500;
  temperatura = voltaje / 10;

}

void Calculo_Tmax_Tmin_Tmedia()
{
  for (int j = 0; j <= 23; j++)
  {
    if (tempdata[j] < tempmin)
    {
      tempmin = tempdata[j];
    }
    else if (tempdata[j] > tempmax)
    {
      tempmax = tempdata[j];
    }
  }
  tempmedia=0;
  for (int j = 0; j <= 23; j++)
  {
    tempmedia=tempmedia+tempdata[j];
  }
  tempmedia=tempmedia/24;
    
}


void show_temperatura()
{

  lcd.setCursor(0, 0);
  lcd.print("Temp actual");
  lcd.setCursor(0, 1);
  lcd.print(temperatura);
  lcd.print(" grados C");
  delay(1000);
}

void Reset_Temp() // Restablecemos todos los valores con el ultimo valor
{  
  tempmin = temperatura;
  tempmax = temperatura;
  for (int j = 0; j <= 23; j++)
  {
    tempdata[j] = temperatura;
  }
}


Si os resulta más cómodo lo podéis descargar desde aquí.

Os dejo un video con el funcionamiento del proyecto. Nos vemos!!







viernes, 8 de mayo de 2015

Termometro diario ( I )

Recuperamos el hilo donde lo dejamos hace ya unos días. Espero que las circunstancias personales me permitan continuar con el ritmo anterior.
Recordemos el último post, donde realizábamos un termómetro con una pantalla LCD de 16x2, que nos mostraba la temperatura.
Vamos a ir un poco más allá, y empezaremos a realizar proyectos que nos permitan interactuar con la información que nos presentan, y a procesar la información según nos convenga.
La propuesta, que atenderemos en varias entradas, es confeccionar un termómetro que guarde un registro de 24 muestras diarias a intervalos de una hora, y que las muestre a voluntad del usuario. Informando también de la temperatura media, mínima y máxima. También debe permitir resetear los valores tomados.

Casi nada.

El interfaz de usuario lo haremos con una pantalla LCD de 16X2 y dos pulsadores. Uno servirá para mostrar la información y además confirmar el borrado de datos. El otro pulsador lanzará el menú de reseteo de muestras, y desestimará la opción de borrar los datos.
La temperatura la calcularemos con nuestro ya conocido TMP-36.

El hardware es bien sencillo, pero para resolver posibles dudas y aclarar el tema de las entradas usadas , mi propuesta es esta.


Una de las primeras dificultades que nos encontramos es donde almacenar las 24 muestras que iremos tomando a lo largo del tiempo. Mi propuesta para resolverlo es a través de una matriz o ARRAY, vamos a ello.

Arrays[]

Un array, o matriz, es un conjunto de valores a los cuales se puede acceder a través de un índice. El primer valor de una matriz está indicado con el numero 0.

Hay varias formas de declarar una matriz. Veamos unos ejemplos:


int array1[5];

int array2[] = {1,3,5,7,9}

int array3[5] = {1,3,5,7,9}




En el caso del array1, declaramos un array de 5 elementos tipo integer (se puede escoger cualquier tipo de variable flor, double,string) , pero no asignamos valores, ya lo haremos más adelante, en cualquier punto del sketch.



El array2, declaramos un array, sin especificar la dimensión, pero sí que les asignamos unos valores. Por tanto a la hora de compilar, leerá el número  de valores y creará un array de dimensión igual al número de valores.



En el ejemplo del array3, declaramos la dimensión del array, y los valores que le asignaremos.

Como ya he comentado el primer índice de una matriz es el 0, por tanto :




int array3[5] = {1,3,5,7,9}

// array3[0] Contiene el valor 1
// array3[4] Conetiene el valor 9
// array3[5] No existe y producir´a un error


Una de las dificultades de los array, es que indexar en lenguaje C un valor "imposible" como el del ejemplo no genera un error, y puede ser un fallo complicado de detectar.

Para asignar un valor a un array, se debe indicar la posición y el valor que queremos guardar. Hay que tener cuidado con asignar valores del mismo tipo del que se ha creado el array, y tener en cuenta que machacaremos el valor que hubiese.




Así cambiar el valor 5 por un 4 tendría la siguiente sintaxis:



    
array3[2] = 4;



Es decir, el numero cinco , que ocupa la posición 2 ( recordad que se empieza contando desde 0), le asignamos el número 4.


Otra utilidad es asignar a una variable el valor de una posición en concreto. ¿Cómo? Pues si queremos asignar a la variable dato el valor de la posición 3:

        dato = array3[3]; // array3[3] Contiene el valor 7



Para el ejemplo que nos ocupa, usaremos el array como si se tratara de una variable circular. Este es uno de los usos más comunes que se le da a este tipo de variable. 

Definiremos un bucle que va rellenando la matriz con los valores captados, y cuando llega al último empieza desde el principio. Si queremos tomar muestras de temperaturas máxima , mínima y media de un día, lo haremos con un array de 24 posiciones.


Esta parte del cuerpo del programa podría quedar algo parecido a esto:



...
...
...


float tempdata [24];//array de tipo float para 24 muestras hora
...
...
...
for (int i=0; i<=23; i++)//Bucle que nos permite tomar 24 muestras
  {
  sensor = analogRead(0); //Sensor TMP36 en pin analogico 0 
  voltaje = (sensor*5000)/1024; //operaciones necesarias
  voltaje = voltaje-500;        // para calculo de temperatura
  tempactual = voltaje/10;  
  tempdata[i]=tempactual;// Grabamos en la posición iesima  del array
                          // la temperatura calculada
  delay (360000); retardo de 1 hora entre muestras
}
...
...
...







Obviando la parte del programa que realiza el cálculo, que ya está explicada aquí, vemos como declaramos una variable array para 24 valores float.

Luego definimos un bucle de 0 a 23, para grabar en cada posición el valor de temperatura obtenido. 

Establecemos un delay de una hora con el fin de conseguir una muestra horaria que es lo que estamos buscando. 

Pero....


¿Que sucederá si aplicamos un delay de una hora? Pues eso, que durante una hora nuestro arduino no atenderá ninguna de sus entradas-salidas, porque estará esperando. Vamos que así no nos sirve.

Sí que hay una manera de interrumpirlo, y es usar uno de los pies de entrada (depende del modelo de Arduino usado), como una interrupción, que saque del delay al equipo. De todas maneras es un método que no me gusta para este ejemplo por lo que lo resolveremos de otra manera.

¿Cómo?

Pues con el uso de la función milis(). Ya la explicamos brevemente en su día en esta entrada

Un breve repaso, esta función cuenta el tiempo que ha pasado desde que se puso en marcha nuestro Arduino. Podemos contar en cada bucle el tiempo pasado y tomar muestras si es mayor de una hora respecto la muestra anterior. Esto nos permitirá con el código necesario, atender también los pulsadores del interfaz de usuario. 

Esta modificación también repercute en el bucle que recorre el array para grabar las muestras. Ahora será necesario crear un "puntero" que indique la posición del array que queremos grabar. Es importante que se mueve solo ente las posiciones 0-23, de lo contrario el comportamiento sería inesperado. 

¿Cómo quedaría el bloque anterior con la modificación?

...
...
...
unsigned long tiempoentremuestras=0; // reseteamos contadores de tiempo
unsigned long tiempomuestraanterior=0;// Este tambien
int posicion=0;//indice o puntero que recorrera el array
...
...


void loop() 

{
  ...
  ...

 tiempomuestraanterior=millis();// leemos el tiempo trasncurrido
 if( tiempomuestraanterior-tiempoentremuestras>360000)//Una hora

 {
  sensor = analogRead(2);       // TMP36 sensor  pin 0
  voltaje = (sensor*5000)/1024; // Calculos necesarios para tempertura
  voltaje = voltaje-500;   // Calculos necesarios para tempertura    
  tempactual = voltaje/10;    // Calculos necesarios para tempertura
  ...
  tempdata[posicion]=tempactual;// Grabamos en el array el dato
  posicion=posicion+1;// Incremetamos el puntero para la siguiente posici´n
  tiempoentremuestras=tiempomuestraanterior;// Resetamos el "cronometro"
  }
  
  if (posicion > 23)
   {posicion=0;} // Volvemos el puntero a 0 cuando pase de la posicion 23
   
...
...
}


¿Alguna duda este bloque? Los nombres de las variables son un tanto extensos, pero nos ayudará a entender mejor el funcionamiento del programa.

Hasta aquí la entrada de hoy. Con esto tenemos resuelto el grabar muestras de temperatura a intervalos de una hora durante un día. Pasadas 24 horas el programa irá machacando la primera muestra, y así sucesivamente, con lo que tendré siempre los datos de las últimas 24 horas.

En la proxima entrada trabajaremos el interfaz de usuario y como conseguir mostrar la información. Gracias por vuestra atención.

Nos vemos!