domingo, 28 de octubre de 2018

Veleta con Potenciometros FCP22E

Hola, a día de hoy(1/1/2018) acabo de sacar del horno un nuevo programa, en esta ocasión se trata de una veleta (la mas sofisticada que he parido) los sensores con los que vamos a trabajar son dos potenciómetros un tanto especiales. El FCP22E
Los potenciómetros son resistencias variables y son muy fiables para convertirlos en sensores de posición, ya que arrojan un valor fijo para una misma posición.
Tiene dos inconvenientes: una limitación mecánica (unos 300º) y su desgaste debido al contacto físico de su patilla interna con la espiral interna (la propia resistencia).
El potenciómetro FCP22E elimina el primer inconveniente ya que permite girar 360º (no tiene limitación física) y respecto al desgaste queda por demostrar cuanto puede durar.
Por la característica de rotación continua podemos confundirlo con un encoder, pero no lo es, un encoder además de poseer una resolución bajísima (los económicos) tienen el inconveniente de ponerse a cero al dejarlo sin suministro eléctrico. Además de esto su posición debe estar controlada continuamente por un programa, si el programa se cierra, la posición del encoder se inicia a cero.
Mi veleta actual funciona con un encoder, y como ya podeís imaginar, había que mejorar el sistema.
Aunque no todo lo que reluce es oro!, los potenciómetros de giro continuo tienen una franja de unos 30º-40º (según el modelo) en los que no lee nada, y ojalá esto fuera así, cuando el FCP22E se encuentra en esa zona muerta arroja valores aleatorios que nada tienen que ver con la realidad. Si bien cabe señalar que este problema se origine en la misma Arduino. Y para comprobar esto podemos hacer una simple prueba, que trataría de cargar un programa que lea un potenciómetro conectado por ejemplo al puerto A0 y no le conectamos nada al mismo, si imprimimos por el monitor Serial veremos que imprime valores aleatorios que cambian a cada momento, lo mismo que sucede cuando el potenciómetro se encuentra en su zona muerta.

Bueno entrando en materia... para poder tener una posición sea cual sea en torno a los 360º grados con estos potenciómetros, mi solución pasa por conectar dos de ellos, el primero leerá de 0º a 180º y el segundo de 180º a 360º.
La configuración física es un tanto entretenida, yo he puesto 3 poleas a 120º una para cada potenciómetro y otra para la veleta. El sistema de poleas simplifica la configuracion ya que al girar la polea de la veleta las otras dos giran solidariamente y en el mismo sentido, si se emplearan engranajes el segundo giraría en sentido contrario al primero y el tercero en sentido contrario al segundo. Lo mas entrenido es posicionar cada potenciómetro en su lugar correcto respecto a la polea de la veleta. (los gráficos ayudan a visualizar esto) Por su parte el programa no esta exento de complicaciones, aunque al final lo he resuelto con tres sencillas condicionales IF, de manera que se ha quedado bastante escueto.
Os dejo unas imágenes del proyecto, y el programa comentado hasta los ojos.
Por cierto las poleas y correa son de maquinas 3D, son económicas y fáciles de encontrar, aunque sería mejor emplear poleas no dentadas como una correa en V por ejemplo, las poleas dentadas me han entretenido bastante a la hora de posicionar los potenciómetros ya que el sistema dentado te obliga a que todas sus poleas coincidan en posición, esto lo he resuelto liberando las poleas de los ejes de los potenciómetros, para después situar el potenciómetro el lugar correcto y entonces fijar las poleas.
Acto seguido dejo un gráfico con cuatro posiciones críticas. La figura 1 muestra la configuración inicial; El potenciómetro CERO debe estar en su posición cero ó inicial y ésta debe coincidir con la veleta apuntando al Norte, el pot UNO debe tener su posición invertida, es decir su posición cero o de inicio debe comenzar cuando el pot CERO se encuentre a 180º. La segunda figura muestra el momento en que el pot UNO entra en su zona muerta, este momento debe estar gestionado en el programa, y en concreto se encuentra en las lineas 18,19 y 20 del mismo. En la figura 3 el pot UNO entra en funcionamiento, mientras que pot CERO deja de estarlo al superar los 180º. Y en la figura 3 se muestra el momento en que el pot UNO entra en su zona muerta, solo que en este caso al estar éste desactivado no es necesaria gestión alguna.




En la siguiente imagen os dejo la conexión eléctrica. es de lo mas sencillito, la patilla izquierda de los pots van a 5v. la derecha a GND y la central a su respectivo puerto analógico. A0 y A1 según el programa. Si invertimos las patillas de positivo a negativo lo que conseguimos es una lectura inversa, es decir al girar su perilla hacia la izquierda su resistencia se incrementará. Así que podemos conectarlas según nos convenga al instalar el grupo de sensores en la veleta.









Por cierto, estos diagramas los hago con Fritzing: http://fritzing.org/home/



Aquí abajo os dejo un foto-montaje para que tengáis una referencia real de como ha quedado. en la cuarta imagen se ve un circulo negro con una chapa dorada, que es el asiento del cojinete, para que la veleta gire lo mas cómoda posible. 



y más abajo el montaje definitivo...



A continuación os adjunto la última versión del sketch, fuertemente comentada:

/*    Central A0 (señal), Izquierdo 5V (+), Derecho 0V (-)   */
void setup() {
  Serial.begin(9600); // Inicia Serial
} // cierra setup

void loop() { // el programa cuenta con una unica instruccion
  Serial.println(calculaGrados()); // Imprime el resultado de la funcion calculaGrados()
} // cierra loop

// La siguiente funcion devuelve un valor de tipo entero a quien la llame, en este caso Serial.println linea 8
int calculaGrados (){ // funcion denominada calculaGrados(), esta funcion determina que potenciometro debe emplear y devuelve su valor.
  int vp0 = analogRead(A0); // introduce en la variable "vp0" el valor de la lectura del pin A0
  int vp1 = analogRead(A1);// introduce en la variable "vp1" el valor de la lectura del pin A1
 // La lectura del potenciometro nos arroja valores comprendidos entre 0 y 1023
 // y a nosotros nos interesan convertirlos de 0 a 360  
  vp0 = map(vp0, 0, 578, 1, 180); // pot CERO arroja el valor 578 en la mitad de su recorrido 
  vp1 = map(vp1, 453, 1000, 180, 360); // pot UNO, tiene su posicion invertida respecto a pot CERO
  // Es necesario leer los valores que arrojan nuestros potenciometros, y colocarlos y en la posicion idonea
  // para indicarle a la funcion Map los limites.    
  if (vp0 >= 1 && vp0 <= 180) { // si los valores leidos del pot CERO se encuentran entre
    return vp0; // el rango 1 y 180, se imprimiran los valores de pot CERO
  }  else {return vp1;} // en caso contrario, nos interesa imprimir los valores de pot UNO 
} // Cierra funcion 
He de confesar que tras unos días de trabajar con él he detectado que se cometían algunos errores en la lectura de los potenciómetros...posiblemente el Sketch está poco currado. Los errores se dan en dos pequeñas franjas justo antes de entrar en las zonas críticas correspondientes de cada potenciómetro. En cualquier caso este proyecto no es el mas indicado para una veleta, como he comentado con anterioridad. La Veleta definitiva a día de hoy esta funcionando con dos sensores de efecto Hall, pero esto es otra historia, que publicaré mas adelante.


domingo, 14 de octubre de 2018

Introducción a la EEPROM, memoria interna de Arduino



En esta entrada vamos a introducirnos en un espacio poco empleado de nuestra arduino, su ROM; la ROM es una memoria interna no volatil ó permanente; esto puede resultar muy útil para guardar información y no perderla en caso de reinicio ó apagado y así poder acceder a ella en cualquier momento. Pero esta característica tan interesante tiene limitaciones; y es que disponemos de tan sólo 1024 bytes de espacio, y nótese que NO he dicho 1Kbyte, aunque sea lo mismo. La explicación está en la profundidad, resolución ó amplitud; Me explico, El Atmega 328 de Arduino dispone de 1024 espacios de un byte cada uno; esto significa que en cada uno de los 1024 espacios solo podemos guardar valores iguales ó inferiores a un byte; (valores de hasta 255 en cualquiera de sus representaciones; números decimales, binarios, Hexa, ASCII, etc.. ) Las aplicaciones del uso de la ROM ó EEPROM (para Arduino) pueden ser muy diversas, en mi caso concreto hago uso de ella en mi estación metereológica para guardar los datos recogidos así como la fecha y hora. Debido a las limitaciones comentadas se requiere cierto ingenio para poder guardar una fecha ó una hora, y este es uno de los motivos para introducir este tema ahora, junto con el posterior (Función definida MAP) puesto que mi objetivo final es mostraros como he construido mi estación metereológica (lectura de Temperatura, Humedad, Presión Atmosférica, Componente del viento, Intensidad del Viento y Pluviometría). Bueno, regresando al asunto, podemos observar alguna curiosidad, a partir de la posición del array 255, donde su valor es 256 y el valor escrito en la EEPROM es 0; como he comentado anteriormente la EEPROM solo puede almacenar valores de hasta 255 (1 byte). Cuando superamos este valor nótese que el programa le da la vuelta al valor y comienza desde cero y de ahí hacia arriba, este comportamiento es muy importante tenerlo en cuenta. Si pretendemos guardar en la EEPROM un valor superior a un byte tenemos varios caminos, os propondré dos (en posteriores entradas) que requerirán de una gestión adicional (usar tantos bytes como sean necesarios ) y otro mas elegante pero con pérdida de resolución (con la función MAP).

El uso ó manejo de la EEPROM requiere incluir la librería EEPROM al principio del Sktech (Linea 2 del adjunto). La gestión de la EEPROM se hace con dos instrucciones:
- PARA ESCRIBIR; EEPROM.write(posición,valor); línea 32. Requiere dos parámetros; el primero es la posición en la cual queremos escribir, va de 0 a 1023, si indicamos 1024 escribirá en la posición 0 y así sucesivamente. El segundo parámetro es el valor que pretendemos escribir o su ubicación.
- PARA LEER; EEPROM.read(posición); lógicamente sólo requiere un parámetro, la posición que queremos leer.

Y ya, ahí va el Sketch:

 #include <EEPROM.h>  // incluye la libreria EEPROM para poder hacer uso de la ROM en nuestro Sketch
// A continuacion se declara un array de tipo "int" con 360 posiciones y se da un valor a cada una de ellas
int valores[360]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,
                  34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
                  64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,
                  94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,
                  118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,
                  141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,
                  164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,
                  187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
                  210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,
                  233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
                  256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,
                  279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,
                  302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,
                  325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,
                  348,349,350,351,352,353,354,355,356,357,358,359,360};                

void setup() {
   Serial.begin(115200);// indicamos una velocidad de comunicacion superior a la habitual(9600), para que imprima mas rapido
   Serial.println(); 
   for(int i=0;i<360;i++){  // bucle para el control de la impresion
      Serial.print("Pos. array n");Serial.write(186);Serial.print(" ");//Serial.write()imprime caracteres especiales
      if(i<10){Serial.print("  ");}// rellena dos espacios en blanco si el valor leido tiene solo un caracter
      if(i<100 && i > 9){Serial.print(" ");}// rellena un espacio en blanco si el valor leido tiene solo dos caracteres
      Serial.print(i);Serial.print(" ");// imprime el valor de "i", dado por el bucle y usado como indice
      Serial.print(" valor ->  [ ");
      if(valores[i]<10){Serial.print("  ");}// rellena dos espacios en blanco si el valor leido tiene solo un caracter
      if(valores[i]<100 && valores[i] > 9){Serial.print(" ");}// rellena un espacio en blanco si el valor leido tiene solo dos caracteres
      Serial.print(valores[i]);Serial.print(" ] ");// imprime el valor del array en la posicion "i" dada por el bucle
      EEPROM.write(i,valores[i]);// Escribe en posicion "i" (dada por el bucle)
      Serial.print(" valor en EEPROM ->  | ");
      if(EEPROM.read(i)<10){Serial.print("  ");}// rellena dos espacios en blanco si el valor leido tiene solo un caracter
      if(EEPROM.read(i)<100 && EEPROM.read(i) > 9){Serial.print(" ");}// rellena un espacio en blanco si el valor leido tiene solo dos caracteres
      Serial.print(EEPROM.read(i));Serial.println(" |");// lee e imprime en la posicion "i" (dada por el bucle) de la EEPROM
   }
}

for(int i=0;i<102;i++){  // bucle para el control de la impresion
void loop() {}