Sensor de humedad y temperatura con Arduino. Cuarta parte: Gadget para Windows 7
Este artículo es una continuación de los siguientes:
- Sensor de humedad y temperatura con Arduino. Primera parte: El hardware.
- Sensor de humedad y temperatura con Arduino. Segunda parte: firmware.
- Sensor de humedad y temperatura con Arduino. Tercera parte: HttpRelay.
Utilizando la base desarrollada en artículos anteriores, se construyó un gadget para el escritorio de Windows 7 que nos muestra información sobre la temperatura y la humedad y cuyo aspecto se puede ver en la figura siguiente.

Además del manifiesto que le define, el gadget está compuesto de Html/CSS, JavaScript e imágenes que definen su background. El siguiente artículo que explica como desarrollar un gadget para Windows resultó muy útil.
Toda la lógica ha sido implementada en JavaScript utilizando Jquery y las APIs de visualización de Google.
El gadget establece un timer que ejecutará una medición de forma periódica cada minuto utilizando ajax. Si la petición tiene éxito se ejecutará la función measureReceived.

En la función measureReceived, ejecutada después de cada medición, se actualizarán los valores de las medidas, así como del gráfico que representa su historia. También se calcularán y actualizarán los estadísticos valor medio, mínimo y máximo de la humedad y temperatura.
El código fuente se adjunta al artículo.
Sensor de humedad y temperatura con Arduino. Tercera parte: HttpRelay.
Este artículo es una continuación de los siguientes:
- Sensor de humedad y temperatura con Arduino. Primera parte: El hardware.
- Sensor de humedad y temperatura con Arduino. Segunda parte: firmware.
El acceso a través del puerto serie resulta inconveniente para el propósito de crear un gadget para el escritorio de Windows 7, por lo que se hace necesario escribir un pequeño programa que nos permita trasladar peticiones HTTP convencionales a peticiones a través del puerto serie. Se ha escrito dicho programa y se ha denominado HttpRelay.

Dicho programa se queda residente como un icono en la barra de notificaciones encargándose de trasladar las peticiones HTTP GET a comandos a través del puerto serie que se haya configurado. De esta forma, podemos comunicarnos con el Arduino a través de HTTP.

El programa se configura a través de un archivo xml que indica la URI en la que se debe escuchar y el puerto serie COM al que se delegará la respuesta, así como, la velocidad en bits por segundo a la que se debe utilizar el puerto.

El programa traslada al puerto serie el parámetro de query llamado ‘q’ por lo que si deseamos obtener mediciones de temperatura y humedad con la configuración anterior, utilizando el comando ‘g’ definido en el firmware, deberemos utilizar la URL: ‘http://localhost:8080/?q=g’
El programa HttpRelay, escrito en c#, utiliza la clase HttpListener para realizar la escucha en una URL determinada. Su código fuente se adjunta en este artículo.
Sensor de humedad y temperatura con Arduino. Segunda parte: firmware.
En un artículo anterior se describió el hardware. En este artículo nos centraremos en el programa que se ejecutará en el Arduino, también llamado firmware.
El programa de Arduino que utilizaremos para obtener la medición de temperatura y humedad del sensor SHT15 proporcionará un sencillo interface a través del puerto serie. Dicho programa está basado en el código de Hobby robotics, aunque ha sido fuertemente modificado para adaptarse a nuestras necesidades.
En el principio del programa se definen las constantes que representan a cada uno de los comandos que utilizaremos del sensor, así como a los pines a los que están conectadas las líneas DATA y SCK del sensor. La función shiftIn nos permite leer un numero arbitrario de bits del sensor.

La función sendCommand contiene toda la lógica que envía un comando al sensor SHT15 siguiendo el protocolo descrito en el artículo anterior.

Después de enviar un comando es necesario esperar unas cuantas decenas de milisegundos a que se realice la medida. Una vez que la medida se ha completado, el sensor avisará poniendo la línea DATA a 0. El esperar el resultado de la medida, es el objetivo de la siguiente función.

Por último, la función getResult obtiene el resultado de la medida del sensor siguiendo el protocolo descrito en el artículo anterior. Dicha medida se obtiene como un entero de 16 bits que será necesario procesar para obtener la medida en las unidades de humedad y temperatura.

Ese es el objetivo de las siguientes funciones printTemperature y printHumidity; envían el comando correspondiente al sensor obteniendo el resultado como un entero de 16 bits, convierten dicho entero a un valor de temperatura en grados centígrados y a un valor de humedad relativa, imprimiendo dichos valores a través del puerto serie.

La última parte del sketch de arduino es la configuración que simplemente establecerá el pin SCK como de salida y configurará el puerto serie a 9600 bps.
y el bucle principal que define una serie de comandos para obtener la medición de la temperatura y la humedad. El comando que utilizaremos en artículos posteriores es el comando ‘g’. Al recibir una ‘g’ en ASCII por el puerto serie, el programa realizara mediciones de la temperatura y de la humedad y las imprimirá por el puerto serie separadas por el carácter ‘|’ y finalizadas por ‘\r\n’.
La implementación de dicho programa en el Arduino ya nos permite obtener los valores de temperatura y humedad utilizando un programa de terminal tal y como se muestra en la figura siguiente.

En un artículo posterior utilizaremos esta base para construir un gadget para el escritorio de Windows 7.
Control de servomotores con Arduino y WPF
Los servomotores son motores que integran una circuitería de control que permiten posicionar su eje dentro de un rango. Típicamente los servomotores permiten posicionar el eje con precisión dentro de un rango angular de 0 a 180 grados aproximadamente.
Constan de tres señales de entrada: alimentación, referencia, y señal de control. Utilizando la señal de control se puede posicionar el motor dentro del rango. El control se realiza a través de pulsos de duración determinada de la señal de control. Por ejemplo, para posicionar un servomotor controlado con pulsos de 1msg a 2msg de rango 0-180 grados a 90 grados necesitaríamos suministrar a la señal de control un pulso de 1.5msg de duración.
Se conecta el control del servomotor a la salida digital número 9 del Arduino que programaremos para recibir mensajes de posicionamiento a través del puerto serie, interpretarles y posicionar el servomotor. Para ello utilizaremos las librerías de control de servos (Servo) y de delimitación de mensajes del puerto serie (Messenger).
La inicialización del programa del Arduino se muestra en la siguiente figura donde simplemente indicamos a la librería de control de servos que se utilizará el pin 9 para el control, inicializamos el puerto serie a 115200 baudios e inicializamos la librería de procesado de mensajes.
Dentro del bucle principal simplemente se leerá el puerto serie esperando mensajes de la forma ‘SERVO n’ donde n es un número de 0 a 178 que indica el ángulo en grados donde se desea posicionar el motor. Una vez interpretado el mensaje satisfactoriamente se posicionara el servomotor y se añadirá un pequeño retraso de 15msg que permita al servomotor posicionarse antes de recibir otra orden.
Para la aplicación WPF diseñaremos una clase con una única propiedad que permita establecer la posición del servo. Haremos que dicha propiedad sea ‘Bindable’ para poderla enlazar de forma natural con los controles de interface gráfico.
La parte principal de dicha clase se muestra en la figura siguiente:
y se creará un interface gráfico para el control en XAML enlazando a dicha propiedad de la siguiente manera:
El resultado se puede ver en el siguiente video. El código fuente de ambos programas se adjunta en el artículo:
Termómetro con Arduino
Utilizando un Arduino y el chip sensor de temperatura TMP36 se construye un sencillo termómetro tal y como se propone en el circuito diez de la guía de experimentación de adafruit.
El circuito es extremadamente sencillo, basta conectar los pins de alimentación y referencia del sensor para alimentarle, y que de esta forma nos proporcione en su otro pin un voltaje proporcional a la temperatura, el cual leeremos a través de la entrada analógica número 0 del Arduino, cuyo ADC nos proporciona una resolución de 10 bits.
El programa cargado en el Arduino es simplemente un bucle que cada segundo lee el voltaje de la entrada analógica y le convierte en un valor de temperatura utilizando las indicaciones de la hoja de características del sensor, enviando posteriormente este valor por el puerto serie.
El programa de PC leerá de forma continua los valores de temperatura que le vayan llegando por el puerto serie virtual (USB) y representándoles en pantalla. Su código principal es el siguiente (Aplicacion WPF en C#):
Con lo cual podremos leer en la pantalla del PC el valor de temperatura procedente del sensor, el cual se actualizará cada segundo.
JavaScript Intellisense con Visual Studio 2010
Parece que la próxima versión de Visual Studio va a traer importantes mejoras en el Intellisense para JavaScript.
En la imagen anterior se aprecia como es capaz de reconocer la introducción de 5 variables de forma dinámica a pesar de los alerts y las excepciones lanzadas.
Publicado el código fuente del programa del Apolo 11
Se ha publicado parte del código fuente del programa que utilizó el Apolo 11 en la primera misión espacial tripulada a la luna.
El programa se puede ejecutar utilizando el emulador del ordenador de a bordo del Apolo 11 proporcionado por el proyecto Virtual AGC y AGS.

Mapas de bits dinámicos en Silverlight
Una de las novedades introducidas en la versión 3 de Silverlight es la clase WriteableBitmap que nos permite la generación dinámica de mapas de bits o bitmaps.
Su funcionamiento es muy sencillo, basta con pasar en el constructor el número de pixels de ancho y de alto que queremos que tenga la imagen para generarla y una vez generada podemos acceder a su contenido a través de la propiedad Pixels.
En el siguiente fragmento de código se construye un bitmap de 320x200 pixels y se establece el pixel en la posición (x, y) al color (r,g,b). Una vez modificado el bitmap se debe llamar a la función Invalidate para actualizar su contenido.
1: var bitmap = new WriteableBitmap(320,200);
2: var pixelColor = BitConverter.ToInt32(new byte[] { (byte)b, (byte)g, (byte)r, 255 }, 0)
3: bitmap.Pixels[x + y * bitmap.PixelWidth] = pixelColor; 4: bitmap.Invalidate();Por supuesto es necesario asignar el bitmap a la propiedad Source de un control Image para representarle en pantalla.
Una funcionalidad interesante añadida es el método Render que permite representar en el bitmap un control gráfico descendiente de UIElement después de aplicarle una transformación (rotación,escala,posición) lo que abre la puerta a la generación de interesantes efectos.
La siguiente ilustración muestra la generación dinámica de bitmaps. Si posa el cursor sobre la imagen podrá ver una representación de metaballs construida sobre un WriteableBitmap.
Representación volumétrica de contenido espectral
Utilizando la técnica descrita en el artículo anterior se ha representado el contenido espectral de una canción en una estructura tridimensional. Para ello se representa el espectro de cada pequeño tramo temporal de la canción en la cara frontal del cubo desplazándose en profundidad a medida que pasa el tiempo y va llegando nuevo contenido espectral.
El resultado se observa a continuación aunque la calidad del video no es demasiado buena y se observan inconsistencias en el framerate que ‘estropean’ la continuidad del efecto.
Cada plano del eje Z o de profundidad representa a un espectro. Cada espectro se representa en cada plano X-Y del volumen modificándose ligeramente de forma aleatoria el contenido del eje Y o altura con objeto de mejorar la estética del efecto. Con el mismo objetivo se aplica una pequeño efecto de escala al volumen que representa la energía promedio del espectro del intervalo de la canción que esta sonando en ese instante.
El sonido pertenece a un fragmento de uno de los episodios de la cuarta temporada de Battlestar Galactica y ha sido extraído de aquí.
Representación de campos escalares tridimensionales
La representación volumétrica es una técnica que permite la representación de campos escalares tridimensionales. Sus aplicaciones son múltiples: representación de imágenes del cuerpo humano procedentes de diversos instrumentos sensores con fines médicos, representación realista de nubes u otros fenómenos gaseosos en simulaciones visuales en entornos virtuales, o representación de diversas estructuras y constructos en la ciencia y la ingeniería.
En este artículo se describirá una variante de la técnica basada en texturas conocida como ‘volume ray casting’. Conceptualmente dicha técnica consiste en el lanzamiento de rayos con origen en la cámara de tal forma que atraviesen el volumen que contiene el campo escalar. El color resultante que se ‘pintará’ en la superficie del volumen en el punto de entrada de cada rayo será una función de los puntos que el rayo atravesó al pasar por el volumen.
Para su implementación se utilizará la plataforma XNA y se hará uso de la aceleración grafica que nos proporcionan la GPU. Será necesaria una tarjeta que soporte al menos la versión 3.0 del modelo de sombreadores (Shader Model 3.0).
El proceso comienza con la representación de un volumen que actuará como frontera del campo escalar, es decir, el campo escalar se encontrará completamente en el interior de ese volumen. Utilizaremos un cubo.
En primer lugar representaremos las posiciones de entrada del rayo y de salida en dos texturas que posteriormente utilizaremos para determinar la dirección. Al Vertex Shader en la GPU se le harán llegar las posiciones sin transformar de los vértices del cubo. Dichas coordenadas se copiaran a la variable InterpolatedPosition a la salida del Vertex Shader. Dicha variable está marcada como coordenadas de textura por lo que a la entrada del pixel shader se recibiran las coordenadas interpoladas. Lo mismo se hará para la variable InterpolatedTransformedPosition solo que en este caso almacenaremos las coordenadas transformadas por la combinación de las matrices de posicionamiento en el mundo (world), vista (view) y proyección (projection) que serán suministradas a la entrada del programa de la GPU como parámetros. Esta última variable será utilizada posteriormente para localizar la posición exacta donde se deben muestrear las texturas para obtener la dirección del rayo.
1 struct VertexShaderInput
2 {
3 float4 Position : POSITION0;
4 };
5
6 struct VertexShaderOutput
7 {
8 float4 Position : POSITION0;
9 float4 InterpolatedPosition : TEXCOORD0;
10 float4 InterpolatedTransformedPosition : TEXCOORD1;
11 };
12
13 VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
14 {
15 VertexShaderOutput output;
16
17 float4 worldPosition = mul(input.Position, World);
18 float4 viewPosition = mul(worldPosition, View);
19 output.Position = mul(viewPosition, Projection);
20 output.InterpolatedPosition = input.Position;
21 output.InterpolatedTransformedPosition = output.Position;
22 return output;
23 }
24
25 float4 RenderPositionsPixelShaderFunction(VertexShaderOutput input) : COLOR0
26 {
27 float4 color = input.InterpolatedPosition;
28 return color;
29 }
Se representarán las caras frontales del cubo en una textura utilizando el estado de representación CullMode.CounterClockWise. El color asignado a la textura en realidad contiene las coordenadas tridimensionades del cubo en el espacio de coordenadas local del modelo en el formato (x,y,z,1).
A continuación se repite la operación pero esta vez representando las caras interiores del cubo.
El siguiente y ultimo paso es la representación del cubo, solo que en esta ocasión el color resultante será función del campo escalar, muestreado en base a las direcciones grabadas en las texturas anteriores. Al muestrear cada una de las texturas anteriores podemos obtener para cada punto (x,y) de la textura, las coordenadas (x,y,z) del cubo por las cuales entró el rayo y por las cuales salió. Con estas dos coordenadas se puede calcular la dirección del rayo y al conocer el punto inicial y la dirección del rayo que atraviesa el campo escalar se puede recorrer cada uno de los puntos de ese campo para calcular su contribución al color de la superficie del cubo. Este proceso se realiza en el Pixel Shader que representará el resultado final en pantalla.
1 float4 RenderVolumePixelShaderFunction(VertexShaderOutput input) : COLOR0
2 {
3 //Calculamos que puntos de las texturas debemos muestrear
4 float2 texC = input.InterpolatedTransformedPosition.xy /= input.InterpolatedTransformedPosition.w;
5 //lo llevamos al rango [0,1] espacio de coordenadas de textura desde espacio de proyeccion 2D
6 texC.x = 0.5f*texC.x + 0.5f;
7 texC.y = -0.5f*texC.y + 0.5f;
8 //Muestreamos las texturas de posiciones iniciales y finales
9 float3 frontPos = tex2D(FrontTextureSampler, texC);
10 float3 backPos = tex2D(BackTextureSampler, texC);
11 //Calculamos el punto inicial y la direccion
12 float4 currentPosition = float4(frontPos,0);
13 float3 direction = normalize(backPos - frontPos);
14 //Inicializamos las variables del color
15 float4 color = float4(0, 0, 0, 0);
16 float4 src = 0;
17 float value = 0;
18 //Recorremos el campo escalar en la dirección calculada
19 //acumulando opacidad en función del campo
20 float3 Step = direction * (1.0f/256.0f);
21 for(int i = 0; i < 256; i++)
22 {
23 //muestreamos la textura
24 value = tex3Dlod(VolumeTextureSampler, currentPosition).r;
25 src = (float4)value;
26 //Front to back blending
27 src.rgb *= src.a;
28 color = (1.0f - color.a)*src + color;
29 //advance the current position
30 currentPosition.xyz += Step;
31 }
32 return color;
33 }
El rendimiento de esta técnica al ser completamente acelerada por GPU es muy bueno como se puede constatar en los siguientes videos obtenidos con una tarjeta gráfica de gama media/baja con un consumo de CPU mínimo.
Basado en un artículo original de Graphic Runner.
Campos escalares obtenidos de vorbis.
Bibliografía: GPU - Based Interactive Visualization Techniques - D. Weiskopf
- 1
- 2
- 3
- 4
- siguiente ›
- última »