Mostrando entradas con la etiqueta montaje. Mostrar todas las entradas
Mostrando entradas con la etiqueta montaje. Mostrar todas las entradas

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!




miércoles, 4 de febrero de 2015

Calibración de sensores (II) Uso de un pulsador

Calibración de sensores (II) .

En esta entrada vamos a explicar varios conceptos que serán de gran utilidad a la hora de realizar proyectos de mayor envergadura.

En concreto y siguiendo con nuestro montaje con el sensor LDR, queremos calibrar nuestro sensor cuando pulsemos un interruptor. De esta manera no interferimos en el resto de procesos que pudiera estar realizando nuestro montaje.

Para ello volveremos a hacer uso de la instrucción While, y haremos uso por primera vez de los procedimientos.


Podríamos modificar nuestro ejemplo anterior y hacerlo por tiempo en el void setup, pero lo que queremos es iniciar el proceso de calibración con la pulsación del pulsador-interruptor. Como siempre , antes de seguir con el programa mostramos el circuito del montaje.



A continuación vamos a definir el algoritmo de nuestro programa:
  • Leemos el valor del LDR
  • Mapeamos la lectura y mandamos la señal PWM equivalente al led de salida
  • Leemos el estado del pulsador, si esta pulsado salvamos los valores máximos y mínimos leídos, de lo contrario, reiniciamos el bucle.
Con la máxima de usar un bucle del tipo While para resolver el ejemplo el sketch podría quedar de la siguiente manera:

/* Ejemplo para calibrar un LDR con un pulsador 
  haciendo uso de la instruccion While
  El circuito analiza el valor en el LDR, y mapea la 
 salida en funcinn de la lectura obtenida.
 Este ejemplo se basa en un adaptacion del que podeis encontrar en

 http://arduino.cc/en/Tutorial/WhileLoop
 
 Mas informacion en castellano en :
 http://arduinopractico.blogspot.com.es
 
 */

// Definicion de constantes
const int ldrPin = A0;       // pin conectado al LDR
const int ledPin = 9;           // pin conectado al LED
const int calibracionLedPin = 13; // Led para indicar el proceso de calibracion
const int pulsador = 2;        // pin concectado al pulsador


// Definicion de Variables
int sensorMin = 1023;  // Valor minimo del sensor
int sensorMax = 0;     // Valor maximo del Sensor
int sensorValor = 0;   // VAlor leido por el sensor

void setup() {
  // set the LED pins as outputs and the switch pin as input:
  pinMode(calibracionLedPin, OUTPUT);//o usaremos para indicar cuando esta calibrando
  pinMode (ledPin, OUTPUT);
  pinMode (pulsador, INPUT);
}

void loop() {
  // Leemos el pulsador y pasamos a calibrar si pulsado
  while (digitalRead(pulsador) == HIGH) {
    calibrado(); 
  }
  // Apgamos led13 cuando acaba el calibrado
  digitalWrite(calibracionLedPin, LOW);  

  // Leemos el sensor
  sensorValor = analogRead(ldrPin);

  // MApeamos el valor del sensor, con los valores
  // obtenidos de la calibracion
  sensorValor = map(sensorValor, sensorMin, sensorMax, 0, 255);

  // Descartamos los valores fuera del rango obtenido en la calibracion
  //restringiendo a la horquilla que heos medido
  sensorValor = constrain(sensorValor, 0, 255);

  // 
  analogWrite(ledPin, sensorValor);
}

void calibrado() 
{
  // Encendemos led 13, para indicar que estamos calibrando
  digitalWrite(calibracionLedPin, HIGH);
  //Leemos el valor del LDR
  sensorValor = analogRead(ldrPin);

  // Grabamos el valor maximo obtenido
  // si supera el que habia grabado
  if (sensorValor > sensorMax) {
    sensorMax = sensorValor;
  }

  // Grabamos el valor minimo obtenido
  //si es inferior al que habia grabado
  if (sensorValor < sensorMin) {
    sensorMin = sensorValor;
  }
}


Lo podeis descargar de aquí.

Las diferencias con ejemplos anteriores las tenemos en la siguiente parte:

// Leemos el pulsador y pasamos a calibrar si pulsado
  while (digitalRead(pulsador) == HIGH) {
    calibrado();


Antes de comenzar el bucle leemos el pulsador, y si está presionado llamamos al procediemiento        "calibrado". Cada vez que comience el bucle y el pulsador continue pulsado ejecutará la rutina de calibrado.
La rutina de calibrado, es exactamente igual a las realizadas hasta ahora. Leemos el valor del sensor, y grabamos los valores máximo y mínimo en las variables sensorMax y sensorMin. Respectivamente.

Ejemplo con dos LEDs fade-in y fade-out


Por último y para acabar de momento con el LDR, propongo otro ejemplo, esta vez con dos LEDs,    comportándose de forma antagónica. Es decir, si bien hasta ahora el LED era proporcional a los          valores de luz leídos, a mayor luz incidente en el LDR, mayor luminosidad del LED, ahora queremos
que uno de los actué de manera inversamente proporcional a la luz incidente.

El montaje consiste en añadir un led más:



Mi propuesta es muy sencilla. Es esta:

/* Ejemplo para calibrar un LDR con un pulsador 
  haciendo uso de la instruccion While
  El circuito analiza el valor en el LDR, y mapea la 
 salida en funcinn de la lectura obtenida.
 Un led sera proporcial (a mas luz mas brillo) y el 
 otro inversamente proporcional, a mayor luz, menor
 brillo.
 Mas informacion en castellano en :
 http://arduinopractico.blogspot.com.es
 
 */

// Definicion de constantes
const int ldrPin = A0;       // pin conectado al LDR
const int ledPin = 9;           // pin conectado al LED
const int led2Pin = 10;           // pin conectado al segundo LED
const int calibracionLedPin = 13; // Led para indicar el proceso de calibracion
const int pulsador = 2;        // pin concectado al pulsador


// Definicion de Variables
int sensorMin = 1023;  // Valor minimo del sensor
int sensorMax = 0;     // Valor maximo del Sensor
int sensorValor = 0;   // VAlor leido por el sensor
int sensorValor2 = 0;  // VAlor para el 2 LED

void setup() {
  // set the LED pins as outputs and the switch pin as input:
  pinMode(calibracionLedPin, OUTPUT);//o usaremos para indicar cuando esta calibrando
  pinMode (ledPin, OUTPUT);
  pinMode (led2Pin, OUTPUT);
  pinMode (pulsador, INPUT);
}

void loop() {
  // Leemos el pulsador y pasamos a calibrar si pulsado
  while (digitalRead(pulsador) == HIGH) {
    calibrado(); 
  }
  // Apgamos led13 cuando acaba el calibrado
  digitalWrite(calibracionLedPin, LOW);  

  // Leemos el sensor
  sensorValor = analogRead(ldrPin);

  // MApeamos el valor del sensor, con los valores
  // obtenidos de la calibracion
  sensorValor = map(sensorValor, sensorMin, sensorMax, 0, 255);

  // Descartamos los valores fuera del rango obtenido en la calibracion
  //restringiendo a la horquilla que heos medido
  sensorValor = constrain(sensorValor, 0, 255);
  sensorValor2 = 255 - sensorValor; // Una vez adaptado el valor,
  //lo invertimos con este truco
  // 
  analogWrite(ledPin, sensorValor);
  analogWrite(led2Pin, sensorValor2);

}

void calibrado() {
  // Encendemos led 13, para indicar que estamos calibrando
  digitalWrite(calibracionLedPin, HIGH);
  //Leemos el valor del LDR
  sensorValor = analogRead(ldrPin);

  // Grabamos el valor maximo obtenido
  // si supera el que habia grabado
  if (sensorValor > sensorMax) {
    sensorMax = sensorValor;
  }

  // Grabamos el valor minimo obtenido
  //si es inferior al que habia grabado
  if (sensorValor < sensorMin) {
    sensorMin = sensorValor;
  }
}



La diferencia, a parte de definir los nuevos elementos que intervienen, es la invertir, uno vez rescalado el valor que daremos al Led 2 con este sencillo truco:

sensorValor2 = 255 - sensorValor; // Una vez adaptado el valor,
  //lo invertimos con este truco


Por cierto podeis obtener este último ejemplo desde aquí.
Un video con el funcionamiento de este último. No olvidéis calibrarlo primero pulsando el interruptor y sometiéndolo a los valores de luz máximo y mínimo.



Nos vemos.

sábado, 24 de enero de 2015

Calibración de sensores (I). Uso de While

Calibración de sensores (I). Uso de While

En la entrada anterior vimos como ajustar un sensor para que actuase en un rango de funcionamiento determinado. El método era muy sencillo y sobretodo visual, lo que nos enseñaba a entender el procedimiento.
Por otro lado, tenía el inconveniente de que necesitas del portátil para calibrarlo, con lo que pierde practicidad e impide que la calibración la pueda realizar cualquiera, y en cualquier caso la complica un poco.
Veamos en esta entrada otro método más autónomo para calibrar sensores, pero primero explicaremos la instrucción que nos ayudará a realizarlo.

While 

La instrucción while, ejecuta una serie de instrucciones, hasta que la condición a testear sea cierta. Este bucle se repetiría indefinidamente hasta que se cumpla, por lo que hay que incluir una condición que pueda variar para que en algún momento se cumpla, de no ser así, entraríamos en bucle infinito, y sería un error de programación.
Esta condición, que debe estar dentro del bucle, puede ser el incremento de una variable o el chequeo de un sensor o entrada del Arduino.
La sintaxis de la instrucción es:

While (expresión){
Acciones en el bucle
}

Un ejemplo , sacado de la web arduino.cc, sería el siguiente:

var = 0;
while(var < 200){
  
// do something repetitive 200 times
  var++;
}

En este ejemplo, se usa la variable while para realizar un bucle que repetirá una acción 200 veces antes de salir del bucle.

Ejemplo calibración


Para nuestro caso concreto, lo que queremos realizar es la calibración de un sensor LDR durante los primeros segundos de puesta en marcha, para luego modular la luminosidad de un LED en función de la luz incidente.
El algoritmo para nuestro montaje sería algo parecido a esto:

  • Crear un bucle de un tiempo definido desde la puerta en marcha
  • Comprobar en este tiempo los valores ofrecidos por el sensor almacenando los valores máximos y mínimos
  • Fuera del bucle, escalar nuestros valores de salida (mapear)en función de los resultados obtenidos y generar la señal de salida correspondiente.

El montaje será idéntico a la entrada anterior, pero refresquémoslo:



El sketch propuesto para resolverlo es el siguiente:


/* Ejercicio Calibracion I. Procedemos a establecer los valores maximos
y mínimos durante los primeros segundos de ejecución del programa
 */

const int ldrPin = A0;    // pin conectado al LDR
const int ledPin = 9;     // pin conectado al LED
const int settime= 5000;  // Tiempo dedicado al ajuste desde
                          // el inicio del programa 
int ValorLDR = 0;         // Valor lectura LDR
int MinLDR = 1023;        // Valor mínimo de la LDR
int MaxLDR = 0;           // Valor máximo de la LDR

void setup() {
  // Encendemos ell LED del Arduino durante la calibración
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);

  //Iniciamos la calibración
  while (millis() < settime) {//Definido en principio para 10 segundos
    ValorLDR = analogRead(ldrPin); // Leemos el valor del sensor

    // Grabamos el valor máximo si es mayor que el guardado
    if (ValorLDR > MaxLDR) {
      MaxLDR = ValorLDR;
    }

    // Grabamos el valor mínimo si es menor que el guardado
    if (ValorLDR < MinLDR) {
      MinLDR = ValorLDR;
    }
  }

  // Apagamos el led 13 pasados los 5 segundos para indicar que 
  // la calibracion ha finalizado
  digitalWrite(13, LOW);
}

void loop() {
  // read the sensor:
  ValorLDR = analogRead(ldrPin);
  
  //Restringimos los valores a los obtenidos durante el calibrado
  ValorLDR = constrain(ValorLDR, MinLDR,MaxLDR);
  
  // Escalamos los valores en la horquilla obtenida en calibracion
  ValorLDR = map(ValorLDR, MinLDR, MaxLDR, 0, 255);

  // Encendemos el LED al valor obtenido con la salida PWM
  analogWrite(ledPin, ValorLDR);
}

El sketch o podeis descargar de aquí.

Esta vez para calibrar el sensor, a diferencia de la entrada anterior bastará con cargar el sketch y durante los primeros segundos ( hemos definido 5s), iluminar y oscurecer el sensor con los valores de trabajo. Transcurrido este tiempo los valores máximo y mínimo quedarán grabados.

Os dejo un video con el procedimiento.



Ya trabajamos con la función milis, en una entrada anterior, pero para refrescar, esta función cuenta el tiempo transcurrido desde la puerta en marcha.

En nuestro bucle While, vamos comprobando los sensores  hasta que transcurra el tiempo previsto.

Otro punto interesante del sketch, es que esta calibración no la realizamos en el cuerpo del programa , void loop, sino en el void setup, por lo que tan solo se realizará en una ocasión durante el arranque del Arduino. Para volver a calibrar el sensor habría que reiniciar el Arduino, con lo que bastaría con pulsar el reset de la placa.

Esta solución, si bien es sencilla, pierde practicidad si se han de calibrar varios sensores, y además implica interrumpir el programa en caso de querer recalibrarlo.

En la siguiente entrada desarrollaremos una versión mejorada, y que serviría para la mayor parte de los montajes.

Nos vemos! 

miércoles, 17 de diciembre de 2014

Trabajando con pulsadores II.

Hola de nuevo.

Quedó pendiente de la anterior entrada dos ejercicios propuestos. Vamos a trabajar el segundo de ellos, pulsar para activar el ciclo y pararlo con otra pulsación sobre el mismo interruptor, que es donde reside la gracia.

Para poder activar y desactivar el led, necesitaremos recordar en qué estado se encontraba la salida, de manera que el Arduino cambie su valor en función de cual fuese su estado anterior.

Para almacenar este estado haremos uso de una variable. Las variables,se almacenan en la memoria del Arduino y pueden contener diferentes tipos de datos. Una variable puede ser del tipo número, texto, booleana, un array, etc. El tipo de contenido que quieres almacenar tienes que decidirlo antes de empezarla a usar, y es importante no sobredimensionarla, puesto que en el momento de declararla, reservará ese espacio en la memoria lo uses o no, y como ya hemos comentado nuestro Arduino tiene una memoria RAM realmente limitada.

Las variables de tipo numérico que se pueden utilizar son:

  • byte: almacena un número natural entre 0 y 255 (1 byte).
  • int: almacena un número entero entre -32768 y 32767 (2 bytes).
  • unsigned int: almacena un número natural entre 0 y 65536 (2 bytes).
  • long: almacena un número entero entre -2147483648 y 2147483647 (4 bytes).
  • unsigned long: almacena un número entero entre 0 y 4294967295 (4 bytes).
  • float: almacena un número decimal con un rango entre -3.4028235·1038 y 3.4028235·1038 (4 bytes).
  • const: especifica que la variable definida no podrá ser cambiada durante el programa, siendo siempre un valor constante:
                                      const float pi=3.1415;

Para este ejemplo también usaremos otro tipo de constante de tipo boolean. La variable de tipo booleana solo puede contener dos estados TRUE o FALSE (verdadero o falso), en función de si se cumple o no la condición. La explicaremos más adelante.

Volviendo al ejemplo, debemos definir en este punto cual queremos que sea el comportamiento. Hemos decidido que ,
  • El montaje empiece con el led apagado,
  • El Arduino "vigila" la entrada en espera de una pulsación. Cuando se produce pone                  el LED a "1".
  • A partir de aquí cambiará el estado del led en cada pulsación que se aplique sobre el pulsador.
Para conseguir este comportamiento, el Arduino debe recordar varias cosas. Una es el estado actual del pulsador. Necesitará comparar con el estado anterior, y comprobar si ha habido una pulsación( o si todavía durase la misma).
Y el estado del led, puesto que deberá apagarlo o encenderlo en función de cómo estaba anteriormente.
Por tanto las variables de nuestro montaje podrían ser las siguientes:

  • valor_actual: leerá y almacenará el estado del pulsador
  • valor_anterior: almacenará cada cambio de estado el estado previo
  • estado_led: almacenará el estado del led, encendido o apagado
El sketch quedaría de la siguiente manera:

// Ejemplo 1_6 _Uso de un pulsador para ON-OFF
// Una pulsación activa el LED, que quedará enecendido
// hasta recibir otra pulsación

const int LED = 9; // pin de salida
const int PULSADOR = 2; //pin para el pulsador

int valor = 0; // variable que almacenará la lectura del pulsador
int old_valor = 0; //variable que almacenará la lectura anterior
// del pulsador. Comparandolas sabremos si ha cambiado
int estado_led = 0; // Variable que almacena el estado del LED
// de salida
void setup() 
{
pinMode(LED, OUTPUT); // Programamas el LED como salida
pinMode(PULSADOR, INPUT); // el PULSADOR como entrada
}
void loop()
{
valor = digitalRead(PULSADOR); // leemos el pulsador
// y comprobamos si ha habido un cambio de estado
// comparandolo con el valor anterior

if ((valor == HIGH) && (old_valor == LOW)){
estado_led = 1 - estado_led;
delay(10);
}
old_valor = valor; // El valor leído ahora ya es el "viejo"
// así que lo almacenamos para la siguiente pulsación

if (estado_led == 1)
{
digitalWrite(LED, HIGH); // Encendemos el led si el estado_led es "1"else {
digitalWrite(LED, LOW);
}
}

 Puedes descargar el sketch aquí.

El montaje es el habitual hasta ahora, pero lo recuerdo por si acaso:




La parte que tiene la"chicha" del programa es esta:

if ((valor == HIGH) && (old_valor == LOW)){
estado_led = 1 - estado_led;

Que estamos haciendo, comparamos la lectura del pulsador con la anterior lectura realizada. Si la lectura actual es "1", y la anterior "0", hay una transición o flanco de subida, por lo tanto una pulsación.
Cómo queremos que a cada pulsación conmute el estado de nuestra salida  usamos un truco de programación para  invertir esa variable. Es la línea:

estado_led = 1 - estado_led;

¿Cómo actuaría si sustituyes estado_led por 0 o por 1?,¿o viceversa? Verás que la fórmula invierte, el valor en los dos casos.

¿Funciona? Sí, este programa lo puedes encontrar muy parecido en algún libro sobre el Arduino. Pero hay una forma más "elegante" de hacerlo. Para ello explicaremos las variables Booleanas.

Variables tipo boolean


Este tipo de variable, que ocupa un byte, sólo puede tomar dos valores TRUE o FALSE, (verdadero o falso). Como en nuestro caso  el led solo puede estar encendido o apagado , podríamos definir la variable estado_led como boolena y reescribir el código de la siguiente manera:


// Ejemplo 1_6boolean _Uso de un pulsador para ON-OFF
// Una pulsación activa el LED, que quedará encendido
// hasta recibir otra pulsación

const int LED = 9; // pin de salida
const int PULSADOR = 2; //Entrada para el pulsador

int valor = 0; // variable que almacenará la lectura del pulsador

int old_valor = 0; //variable que almacenará la lectura anterior
// del pulsador. Comparandolas sabremos si ha cambiado
boolean estado_led = false;//En este caso cambiamos la variable int por
// una del tipo boolean. Es la que almacena el estado del LED.La ponemos
// =, o FALSE

void setup() 
{
pinMode(LED, OUTPUT); // Programamas el LED como salida
pinMode(PULSADOR, INPUT); // el PULSADOR como entrada
}
void loop()
{
valor = digitalRead (PULSADOR); // leemos el pulsador
// y comprobamos si ha habido un cambio de estado
// comparandolo con el valor anterior

if ((valor == HIGH) && (old_valor == LOW)){
estado_led = !estado_led;// El símbolo ! invierte el estado
//de la bariable de tipo boolean
delay(20);
}
old_valor = valor; // El valor leído ahora ya es el "viejo"
// así que lo almacenamos para la siguiente pulsación

if ( estado_led == true)//El otro cambio, ahora verificamos si el 
//estado es TRUE que equivale a "1" o HIGH
{
digitalWrite(LED, HIGH); // Enecendemos el led si estado_led TRUE
else {
digitalWrite(LED, LOW);
}
}

Lo puedes descargar desde aquí.

¿Donde están las diferencias?
1.- En la definición de las variables, ahora estado_led es boolean.
      boolean estado_led = false;
2.- En como se invierte la variable estado_led, ahora sé usa el simbolo !.
      estado_led = !estado_led;
3.- Cuando se analiza la condición, ahora se comprueba si es TRUE or FALSE. en lugar de "1" o "0".
      if ( estado == true)

Una nota más antes de dejaros con el video.

Veréis que hay un delay por ahí en medio. Este delay hace las veces de condensador en el montaje de las resistencias PULL-DOWN. Es decir, si lo montáis sin condensador, y no ponéis este delay, notaréis que tiene un funcionamiento errático, puesto que el Arduino es tan rápido en la ejecución del programa que puede leer los rebotes del pulsador. Por tanto lo "entretenemos" con un delay para que pase el rebote del pulsador antes de la siguiente lectura. Si queréis comprobarlo, anular el delay o bajar el tiempo y lo podréis valorar.

Yo he tenido rebotes con valores de delay por debajo de 10ms.

Ahora sí, hasta la siguiente entrada. Nos vemos.

sábado, 13 de diciembre de 2014

Otro ejemplo con PWM. Fade-in y fade-out en un led.

Veamos otro ejemplo con el uso de PWM.

El enunciado del ejemplo sería programar un bucle que encienda un led de manera progresiva hasta su máxima luminosidad y lo apague de la misma manera. Pretendemos simular algo parecido al comportamiento de un led en standby de un portátil.
Para ello, utilizaremos el bucle for que ya vimos en pasadas entradas y la función analogWrite().
Usaremos el mismo montaje de la entrada anterior.

El algoritmo del programa  sería algo como esto:
  • Inicializar entradas y salidas.
  • Hacer un bucle ascendente de 0 a 255 (valor máximo y mínimo) que incremente la salida analógica.
  • Hacer un bucle descendente que de 255 a 0 que vaya apagando el led.
  • Repetir indefinidamente...
El programa, perdón el sketch, quedaría:

//Ejemplo 1_5. Encendido y apagado progresivo de un led
// Modificaremos su luminosidad actuando sobre el ciclo de carga
// de la seña de salida

const int LED = 9; // Pin conectado al LED
int i = 0; // Variable que usaremos para el bucle for

void setup() 
{
  pinMode(LED, OUTPUT); // Configuramos el pin como salida
}
void loop()
{
  for (i = 0; i < 255; i++) // bucle de 0 a 254 (fade in).Vamos
                            // incrementando el ciclo de trabajo
  {                         //de 0 a 255     
  analogWrite(LED, i); // esta línea configura el brillo del LED
  delay(10); // Colocamos una espera de 10ms para poder apreciar
             // el efecto. De otro modo sería tan inmediato que
             //no lo veríamos
  }//fin del primer for.   
  for (i = 255; i > 0; i--) { // bucle de 255 a 1 (fade out)
  analogWrite(LED, i); // esta línea configura el brillo del LED
  delay(10); // Espera de 10ms
  }
}



Lo podéis encontrar para descargarlo aquí:

El código es bastante sencillo, así que vamos a añadir un mínimo de complejidad. Vamos a modificar el ejemplo para que sea la activación de un pulsador la que inicie el ciclo.

Cómo sería el algoritmo en esta ocasión:


  • Inicializar entradas y salidas.
  • Leer el estado del pulsador e iniciar el ciclo si se pone a “1”, (se pulsa)
  • Hacer un bucle ascendente de 0 a 255 (valor máximo y mínimo) que incremente la salida analógica.
  • Hacer un bucle descendente que de 255 a 0 que vaya apagando el led.
  • Repetir indefinidamente…

Ok, sencillo ¿no? Solo hemos añadido una línea  en el algoritmo, y ¿en el sketch? Dad le una vuelta antes de seguir leyendo…
…Una vuelta más…
… ¿Ya? Bien pues aquí mi propuesta:


//Ejemplo 1_5b. Encendido y apagado progresivo de un led
// modificando su luminosidad actuando sobre el ciclo de carga
// de la seña de salida. El ciclo lo inicia una pulsación

const int LED = 9; // Pin conectado al LED
const int pulsador = 2 ; //Pin conectado al pulsador
int i = 0; // Variable que usaremos para el bucle for

void setup() 
{
  pinMode(LED, OUTPUT); // Configuramos el pin como salida
  pinMode(pulsador, INPUT); //Configuramos el pin como entrada
}
void loop()
{
    digitalWrite (LED, LOW); //Fijamos la condición inicial del LED
if ( digitalRead(pulsador) == HIGH ) 
  {
    for (i = 0; i < 256; i++) // bucle de 0 a 255 (fade in).Vamos
                            // incrementando el ciclo de trabajo
    {                         //de 0 a 255     
    analogWrite(LED, i); // esta línea configura el brillo del LED
    delay(10); // Colocamos una espera de 10ms para poder apreciar
               // el efecto. De otro modo sería tan inmediato que
               //no lo veríamos
     }//fin del primer for.   
     for (i = 255; i > 0; i--) 
     { // bucle de 255 a 1 (fade out)
     analogWrite(LED, i); // esta línea configura el brillo del LED
     delay(10); // Espera de 10ms
     } // fin del segundo for
    }//Fin condición de la pulsación
    digitalWrite (LED, LOW); //Apagamos el LED en espera de otro pulso
}


¿En qué varía el código? Bien de entrada fijamos que cuando se ejecuta el bucle, el LED esté apagado en espera de recibir una pulsación.

       digitalWrite (LED, LOW);

Seguidamente ponemos una condición con un IF, (ya lo trabajamos aquí), para detectar un flanco positivo en la entrada, (vamos una pulsación):

        if ( digitalRead (pulsador == HIGH ))

Y pasamos a copiar el código anterior. Tan solo debemos recordar salir del bucle volviendo a apagar el LED en espera de otra pulsación.

         digitalWrite (LED, LOW);

El sketch para descarga lo puedes encontrar aquí.

Este montaje tiene muchas posibilidades. Podríamos intentar por que tras la pulsación repita el ciclo n número de veces y se apague.

Otra aún más interesante, que una pulsación comience el ciclo y otra lo apague (con un solo pulsador).  Os dejo con el vídeo del funcionamiento de los dos ejemplos, y en la próxima entrada la solución a estas dos propuestas. Nos vemos.


lunes, 8 de diciembre de 2014

Modulación por ancho de pulsos. PWM en Arduino

Hola de nuevo,

Bueno una semana sin ordenador, así que una semana casi en blanco. Vamos al lío.

Hoy vamos a explicar un nuevo concepto. Para realizar esta entrada necesitaremos:

- 1 x arduino UNO o compatible,
- 1  x Resistencia de 220ohms, si empezáis podéis comprar un lote de varios valores, por ejemplo como este lote de 400 resistencias, por poco más de 2 €
- 1 x Led, del color que más os guste. Os recomiendo un kit de varios colores si todavía estáis empezando,
- 1 x Protoboard, o placa de prototipos, para realizar los montajes de forma sencilla y sin soldaduras.

PWM, (pulse-width modulación), o modulación por ancho de pulsos.

Esta técnica modifica el ciclo de trabajo de una señal periódica, es decir la relación entre el tiempo que está en su parte positiva y el que está en su parte negativa. La fórmula que lo rige:

Donde:


D = \frac{\tau}{T}



D es el ciclo de trabajo
\tau es el tiempo en que la función es positiva (ancho del pulso)
T es el período de la función

Un gráfico de ejemplo:


¿Qué usos se le da a este tipo de señales? Algunos de ellos son:

- Generar señales de audio.
- Variar la velocidad de los motores
- Generar una señal analógica , si se filtra la salida digital
- Generar una señal modulada, para por ejemplo pilotar un LED infrarrojo de un control remoto,
- Variar la luminosidad de un led,

Hay muchas aplicaciones que se puede dar a este tipo de modulación de señales.

Para nuestro ejemplo vamos a diseñar un "dimmer" muy sencillo que ilumine el led al 10%, variando el ciclo de carga de la señal que haremos llegar al led. Lo que hará nuestro algoritmo es encender y apagar el LED, pero con una frecuencia tan alta, que nuestro ojo no percibe el parpadeo, sino una menor luminosidad. Variando el ancho del pulso incrementaríamos el ciclo de carga, pasaría mayor tiempo encendido y por tanto dará la sensación de mayor luminosidad.

El hardware del ejemplo, será nuestro archiconocido LED rojo, conectado al Arduino en su pin 9 a través de una resistencia de 220Ω. Por si hay alguna duda, algo como esto:


El algoritmo de lo que queremos hacer sería:

- programar el led 9 como salida
- Encender el led, ponerlo a "1"
- Esperar durante 100  μs,
- Apagar el led,
- Esperar durante 900 μs,
- Repetir indefinidamente,

Una propuesta para nuestro programa sería esta:


void setup()
{
  pinMode(9, OUTPUT); // Definimos el pin 9 como salida
}
void loop() {
  digitalWrite(9, HIGH); // Ponemos el pin a 1
  delayMicroseconds(100);// Aprox un ciclo de carga del 10% para un 1KHz
  digitalWrite(9, LOW);
  delayMicroseconds(900);
}


Puedes descargarlo aquí.

Ejemplo 1_4 dimmer con delays

El programa es muy sencillo, y podemos realizar pruebas con la luminosidad final del led, tan solo variando los tiempos de delay.


Así si queremos una luminosidad del 50%, generaríamos una onda cuadrada con un ciclo de carga del 50%, es decir colocando 500 y 500 en cada delay.  Para un 25%, 250 y 750 respectivamente , y así para cualquier combinación que queramos. Recuerda volver a cargar el programa después de cada cambio para hacerlo efectivo.

Pros y contras

Dejando de lado la incomodidad de cambiar el ciclo a mano y volver a cargar el programa, esta manera de trabajar generando la onda de esta manera, tiene varios inconvenientes:

- El ciclo de trabajo está calculado aproximadamente  y se vería afectado por cualquier interrupción en el programa, a no ser que las anulemos, y si las deshabitamos...

- El Arduino no podrá hacer otra cosa mientras se ocupe del tren de impulsos, por lo que no resulta excesivamente práctico.

Algo positivo que se me ocurre de esta manera de proceder, es que podemos implementarlo en cualquier salida digital del Arduino.

Veamos entonces otra forma de implementarlo.

analogWrite()

Esta función tiene la siguiente sintaxis:

analogWrite (pin,valor)

Donde los parámetros son:

pin: el pin que usaremos,
valor: ciclo de carga, entre 0 y 255. (0 siempre apagado, 255 siempre encendido, 127 al 50%, etc...)

Esta función genera en el pin especificado una señal cuadrada cuyo ciclo de carga variará entre entre 0 y el 100% (valores entre 0 y 255). En el Arduino UNO los pins que pueden ejecutar esta función son los pins 3, 5, 6, 9, 10 y 11. 

La frecuencia de la señal cuadrada es de aproximadamente 490 Hz, excepto en los pies 5 y 6 que será de aproximadamente 980 Hz.  

Como quedaría el programa entonces:

const int ledPin = 9;
const int DutyCicle = 255;/* Usaremos un valor de 25 para un 10%, 
67 para un 25%, 127 para un 50% y 255 para un 100% */

void setup()
{   
  pinMode(ledPin, OUTPUT); // Definimos el pin 9 como salida 
}
void loop()
{
  analogWrite(ledPin,DutyCicle);//asignamos pin 9 un ciclo carga del 10% aprox
                    //el valor de un 10% estaría entre un valor de 25
}


Puedes descargarlo aquí:

Ejemplo 1_4b dimmer con analogWrite

Ahora podemos variar nuestra luminosidad (ciclo de carga o duty cicle), tan solo variando un parámetro. Prueba los siguientes valores:

- 0, siempre apagado,
- 63, un 25% de luminosidad,
- 127, un 50%
- 255, un 100% siempre encendido,

Recuerda volver a cargar el programa tras el cambio de valor.

Un apunte más, este tipo de control sobre la luminosidad de un LED, (o cualquier tipo de carga) es mucho más eficiente que hacerlo intercalando un potenciómetro, puesto que aquí no hay perdida de potencia en forma de disipación de calor que si se daría en un potenciómetro o carga resistiva.

Un video de ejemplo: