Mostrando entradas con la etiqueta Tema 1 programación y electrónica. Mostrar todas las entradas
Mostrando entradas con la etiqueta Tema 1 programación y electrónica. Mostrar todas las entradas

viernes, 15 de agosto de 2014

Python básico para estadística.

Python básico para estadística.

Categoría: 2. Ciencia y tecnología.

En esta entrada se presenta un ejercicio básico de estadística utilizando como herramienta el lenguaje de programación Python.

En un principio se trata de elaborar un programa mínimo en Python que pueda correrse sin dificultades. A continuación se agregan funciones para obtener un código más complejo.

Si encuentras de interés esta información, tal vez te sea útil visitar una entrada donde resuelvo problemas parecidos utilizando MATLAB: Análisis de varianza para un blog.

Se puede trabajar en la ventana de Python para realizar cálculos relativamente simples. Cálculos repetitivos se pueden manejar con funciones, que están activas solamente durante el tiempo que dura la sesión. Puede realizar por ejemplo los siguientes cálculos con números binarios:

>>> y=0b0001
>>> for i in range(1,5):
...             y=y<<1
...             print y
...
2
4
8
16
>>>

El primer paso es entonces crear un archivo de texto con el nombre estadistica_1.txt. Selecciona un destino donde puedas guardar estos archivos, por ejemplo, una memoria USB en la unidad E:(E:/estadistica_codigo.txt). En esta unidad también es recomendable crear una carpeta llamada python donde guardar los archivos con extensión .py: E:/python/.

También vas a utilizar el directorio raíz de la unidad E: para guardar los archivos de salida. 

Algunas instrucciones previas para explicar lo que vamos haciendo:
- En Python se coloca un signo hash (#) para iniciar un comentario.
- Se deben llamar las librerías necesarias que contengan las funciones que ocupamos en el código. Esto se hace al inicio, después del nombre del programa y los créditos.
- Se pueden crear funciones que se ejecutan desde la ventana principal de Python.
- La indentación es muy importante para que se ejecute el código.
- La instrucción print despliega los valores de salida en la ventana de sistema.
- En el manejo de archivos no olvides utilizar las instrucciones de apertura, f=open('file.txt',"w") y de cierre, f.close().

Ejercicio en la ventana principal de Python. Después de la primera instrucción, def writefile(): se debe pulsar la tecla tab al inicio de la siguentes instrucciones; las instrucciones que estén dentro del ciclo for necesitan dos tab de indentación. Escribir una función que envía datos a un archivo de salida:

>>> def writefile():
... f=open("E:/myfile.txt","a")
... f.write("\n")
... for i in range(-10,10):
... i=i**3
... print i,"\t"
... v=str(i) # Convierte int a cadena
... f.write(v)
... f.write("\t")
... f.close() 
...

Es importante mencionar algunos modos en que puede abrirse un archivo para escribir datos.

Escribir datos en archivo de salida:
- Función: open() # deveulve un objeto archivo
- open(nombre_de_archivo, modo)

modo acción
'r' Lectura
'w' Escritura
'a' Abre archivo para agregar información. 
El dato se agrega automáticamente al final.
'r+' Abre archivo tanto para lectura como para escritura.

Ahora vas a preparar el mismo programa en un archivo con extensión .py para poderlo ejecutar de manera repetitiva sin necesidad de escribirlo cada vez, como lo haces en la ventana principal de Python. Lo primero que destaca es que se elimina la linea con el nombre de la función - def writefile(): - y también se agrega la instrucción raw_input() al final del código.

f=open("E:/myfile.txt","a")
f.write("\n")
for i in range(-10,10):
i=i**3
print i,"\t"
v=str(i) # Convierte int a cadena
f.write(v)
f.write("\t")
f.close() 
raw_input()

Este código lo capturas en un archivo de texto: E:/vector_Codigo.txt; y guardas. Ahora seleccionas [Archivo] [Guardar como], te desplazas a la carpeta E:/python/ y guardas el archivo como E:/python/vector.py. Recuerda colocar las indentaciones (un tab) en la cinco instrucciones dentro del ciclo for.

Es importante que lleve la extensión .py, de lo contrario no podrá ejecutarse tu código. La máquina donde se corre el programa debe tener instalado Python 2.7. En el explorador de archivos seleccionas el archivo vector.py y das [Enter]. Aparece una ventana de sistema desplegando los resultados. Das [Enter] para cerrar esta ventana de salida y te desplazas en el explorador de archivos hasta E: Ahí seleccionas el archivo myfile.txt y das [Enter]. Entonces puedes ver los resultados de tu primer programa. 

Nota que para este programa sólo utilizas funciones instaladas en la librería principal. Por eso no se incluyeron bibliotecas en el inicio del código.

******************************           *************************************
En el siguiente ejemplo se parte de tres conjuntos de datos, vectores dataset_1, dataset_2 y dataset_3, y se realizan los cálculos de la prueba de Wilcoxon y la prueba de Kruskal-Wallis.

La prueba de Wilcoxon se utiliza para poner a prueba la hipótesis nula Ho: mu1=mu2, contra la hipótesis alternativa H1: mu1 != mu2. Si el valor calculado para la prueba, w_calculada, es menor o igual que w_alfa, la hipótesis nula se rechaza (en este caso != se lee "no igual o diferente a").

Esta prueba solo se utiliza para la comparación de datos en pares. En el ejemplo, la prueba se realiza sobre los conjuntos de datos dataset_1 vs dataset_2 y sobre dataset_2 vs dataset_3.

Puedes ver el código de Programa1 al fina de esta página. La figura siguiente muestra la ventana de salida.


Salida de Programa1.

Para n=10 y alfa=0.05 el valor de w_alfa es w_alfa=8; para la prueba de dos extremos (este valor lo obtienes de tablas estadísticas, valores para la prueba de Wilcoxon).

En la comparación de los conjuntos dataset_1 vs dataset_2 la prueba genera los siguientes resultados: 8.5, 0.05. El primero corresponde a w+ y el segundo a w-. Como el menor de estos valores es menor que w_alfa se rechaza Ho: mu1=mu2 para dataset_1 vs dataset_2. El siguiente resultado muestra 0.0, 0.01, se rechaza Ho: mu1=mu2 para dataset_2 vs dataset_3.

La prueba de Kruskal-Wallis se utiliza para comparar los promedios de dos o más conjuntos de datos, que pueden ser los resultados de tratamientos diferentes en un experimento. Cuando se presume que los datos no muestran una distribución normal. El estadístico de prueba, H, se compara con el valor de Xi^2 con significancia alfa y a-1 grados de libertad. Donde a es el número de conjuntos de datos o tratamientos de donde provienen las muestras. Si el valor de H es mayor que Xi^2,alfa,a-1, Ho se rechaza. Se trata de probar Ho: mu1=mu2= ... =mu_n.

El resultado que muestra el programa despliega el valor de S^2 y de H. En el ejemplo S^2=22.5 y H=1.3e-5. Como el valor de X^2,0.05,2 es de 5.99, el estadístico de prueba cae dentro de la zona de rechazo de Ho. De manera que dataset_1 != dataset_2 != dataset_3.

La curtosis es un valor que indica la forma en que se distribuyen los datos alrededor de la media. Un valor elevado indica que los datos se concentran cerca de la media. En este ejemplo se tienen valores bajos para la curtosis. Esto se explica por la gran dispersión alrededor del promedio, como lo indican los valores altos de la varianza.

******************************            *************************************

En el ejemplo a continuación, se presenta el análisis de varianza para los tres conjuntos de datos que se utilizaron en el ejemplo anterior.

Puedes ver el código de Programa2 al fina de esta página. La salida del programa tiene el aspecto siguiente:


Salida de Programa2:

El resultado del análisis de varianza muestra que el valor del estadístico de prueba es F=204.1; el otro es el valor del estadístico p, que en este caso es p=5.01e-17. Para una prueba con significancia alfa=0.05 la hipótesis nula Ho: mu1=mu2=mu3 se rechaza. En este caso es claro por el elevado valor de F, marcadamente dentro de la zona de rechazo. El valor de p<<0.05 indica que el estadístico de prueba quedó bastante dentro de la zona de rechazo de Ho.

Debe recordarse que en esta prueba debe rechazarse Ho: tao_1=tao_2= ... tao_n=0, si f_obs > f_alfa_a-1_a(n-1).

El valor F con n-1 grados de libertad en el numerador y a(n-1) grados de libertad en el denominador y significancia alfa=0.05, que para este caso es F0.05,2,27=3.35, comparado con el valor f_obs=204.1 nos dice que se debe rechazar Ho.

******************************            *************************************

Ahora una función que crea un vector de nombre A y lo llena con valores generados aleatoriamente.

Puedes ver el código de Programa3 al fina de esta página. La salida del programa tiene el aspecto siguiente:


Salida Programa3.

Los valores de salida son diferentes cada vez que se corre el programa. De manera que nunca vas a obtener los mismos resultados que aparecen en esta imagen.

El programa genera un conjunto de 1000 valores con un generador de números aleatorios con distribución normal. En la salida se muestran los estadísticos que describen al conjunto. Aparece la media, mediana, mínimo, máximo, varianza, sesgo, curtosis y percentil 90.

El sesgo es un valor de la asimetría que se aplica a un histograma o a una distribución de probabilidad. Como la curtosis, es un valor que describe la simetría de un conjunto de datos alrededor del promedio. Su valor puede ser positivo, negativo, cero o inclusive una indefinición. Su interpretación no es muy directa, pero en una distribución unimodal un valor negativo del sesgo indica que hay una cola más alargada en el lado izquierdo de la función de densidad de probabilidad.

*************************                               ****************************

Conclusiones.

Los estadísticos que describen un conjunto de datos pueden calcularse de manera sencilla en python.

El análisis de varianza para hacer comparaciones entre las varianzas de varios conjuntos de datos para saber si provienen de poblaciones independientes, se puede hacer con python de manera rápida.

Una vez que se corren algunos ejercicios en python se puede experimentar con programas más complicados.

Visita la página web de python. Ahí puedes descargar python 2.7, por ahora la versión más reciente.

También puedes visitar la página web de SciPy, donde puedes descargar librerías: NumPy, SciPy, SymPy, MatPlotLib y Pandas.





Bibliografía.

Montgomery, Douglas C., and George C. Runger. 2003. Applied statistics and probability for engineers. Third edition. John Willey $ Sons Inc. United States of America.

Cárdenas Montes, Miguel. Estadística. Site: http://wwwae.ciemat.es/~cardenas/curso_MD/Estadistica.pdf


************************************                     ******************************
************************************                     ******************************
Código Programa1:

# Practica: Estadistica inferencia
# Se trabaja sobre los mismos datos que Estadistica basica
# Prueba de Kruskal-Wallis
# Prueba de Wilcoxon con signo

import scipy.stats

dataset_1=[326.4,324.9,316.3,302.8,317.0,325.4,311.9,322.4,312.1,322.2]
dataset_2=[299.0,301.9,309.4,316.9,304.8,314.2,305.3,308.9,312.5,308.1]
dataset_3=[264.8,275.9,259.0,270.6,266.7,261.8,266.1,268.4,266.2,267.4]

print 'dataset_1: ',dataset_1,'\n','Media: ',scipy.mean(dataset_1)
print 'dataset_2: ',dataset_2,'\n','Media: ',scipy.mean(dataset_2)
print 'dataset_3: ',dataset_3,'\n','Media: ',scipy.mean(dataset_3)

f=open('E:/Estadistica_Inferencia.txt','a')

f.write("Dataset_1: " + str(dataset_1) + '\n')
f.write("Media dataset_1: " + str(scipy.mean(dataset_1)) + '\n')
f.write("Dataset_2: " + str(dataset_2) + '\n')
f.write("Media dataset_2: " + str(scipy.mean(dataset_2)) + '\n')
f.write("Dataset_3: " + str(dataset_3) + '\n')
f.write("Media dataset_3: " + str(scipy.mean(dataset_3)) + '\n')

print 'dataset_1 Median: ',scipy.median(dataset_1)
f.write("Dataset_1 Median: " + str(scipy.median(dataset_1)) + '\n')
print 'dataset_2 Median: ',scipy.median(dataset_2)
f.write("Dataset_2 Median: " + str(scipy.median(dataset_2)) + '\n')
print 'dataset_3 Median: ',scipy.median(dataset_3)
f.write("Dataset_3 Median: " + str(scipy.median(dataset_3)) + '\n')

print 'dataset_1 Min: ',min(dataset_1)
f.write("Dataset_1 Min: " + str(min(dataset_1)) + '\n')
print 'dataset_2 Min: ',min(dataset_2)
f.write("Dataset_2 Min: " + str(min(dataset_2)) + '\n')
print 'dataset_3 Min: ',min(dataset_3)
f.write("Dataset_3 Min: " + str(min(dataset_3)) + '\n')

print 'dataset_1 Max: ',max(dataset_1)
f.write("Dataset_1 Max: " + str(max(dataset_1)) + '\n')
print 'dataset_2 Max: ',max(dataset_2)
f.write("Dataset_2 Max: " + str(max(dataset_2)) + '\n')
print 'dataset_3 Max: ',max(dataset_3)
f.write("Dataset_3 Max: " + str(max(dataset_3)) + '\n')

print 'dataset_1 Var: ',scipy.var(dataset_1)
f.write("Dataset_1 Var: " + str(scipy.var(dataset_1)) + '\n')
print 'dataset_2 Var: ',scipy.var(dataset_2)
f.write("Dataset_2 Var: " + str(scipy.var(dataset_2)) + '\n')
print 'dataset_3 Var: ',scipy.var(dataset_3)
f.write("Dataset_3 Var: " + str(scipy.var(dataset_3)) + '\n')

print 'dataset_1 Sesgo: ',scipy.stats.skew(dataset_1)
f.write("Dataset_1 Sesgo: " + str(scipy.stats.skew(dataset_1)) + '\n')
print 'dataset_2 Sesgo: ',scipy.stats.skew(dataset_2)
f.write("Dataset_2 Sesgo: " + str(scipy.stats.skew(dataset_2)) + '\n')
print 'dataset_3 Sesgo: ',scipy.stats.skew(dataset_3)
f.write("Dataset_3 Sesgo: " + str(scipy.stats.skew(dataset_3)) + '\n')

print 'dataset_1 Kurtosis: ',scipy.stats.kurtosis(dataset_1)
f.write("Dataset_1 Kurtosis: " + str(scipy.stats.kurtosis(dataset_1)) + '\n')
print 'dataset_2 Kurtosis: ',scipy.stats.kurtosis(dataset_2)
f.write("Dataset_2 Kurtosis: " + str(scipy.stats.kurtosis(dataset_2)) + '\n')
print 'dataset_3 Kurtosis: ',scipy.stats.kurtosis(dataset_3)
f.write("Dataset_3 Kurtosis: " + str(scipy.stats.kurtosis(dataset_3)) + '\n')

KW_test=scipy.stats.kruskal(scipy.array(dataset_1),scipy.array(dataset_2),scipy.array(dataset_3))

Wi_test_1vs2=scipy.stats.wilcoxon(dataset_1,dataset_2)
Wi_test_2vs3=scipy.stats.wilcoxon(dataset_2,dataset_3)

print 'Kruskal-Wallis test: ',KW_test

f.write("Kruskal-Wallis test: " + str(KW_test) + '\n')

f.write("Wilcoxon signed-rank test dataset_1 vs dataset_2: " + str(Wi_test_1vs2) + '\n')
f.write("Wilcoxon signed-rank test dataset_2 vs dataset_3: " + str(Wi_test_2vs3) + '\n')

print 'Wilcoxon signed-rank test dataset_1 vs dataset_2: ',Wi_test_1vs2
print 'Wilcoxon signed-rank test dataset_2 vs dataset_3: ',Wi_test_2vs3

f.write('\n')
f.close()


raw_input()

*******************************                          ****************************
Código Programa2:

# Practica: ANOVA
#
# print 'X= %5.3f' % X[i]

import numpy as np
import scipy.stats
import random
from numpy import array
from scipy.stats import kstest

dataset_1=[326.4,324.9,316.3,302.8,317.0,325.4,311.9,322.4,312.1,322.2]
dataset_2=[299.0,301.9,309.4,316.9,304.8,314.2,305.3,308.9,312.5,308.1]
dataset_3=[264.8,275.9,259.0,270.6,266.7,261.8,266.1,268.4,266.2,267.4]

f=open('E:/Estadistica_ANOVA.txt','a')
anova_ans=scipy.stats.f_oneway(dataset_1,dataset_2,dataset_3)
print 'ANOVA: ',anova_ans

f.write('ANOVA: ')
w=str(anova_ans)
f.write(w)
f.write("\n")

# ***
f.write('dataset_1: ')
v=str(dataset_1)
f.write(v)
f.write("\n")

f.write('dataset_2: ')
v=str(dataset_2)
f.write(v)
f.write("\n")

f.write('dataset_3: ')
v=str(dataset_3)
f.write(v)
f.write("\n")

# ***
v=scipy.mean(dataset_1)
print 'Mean dataset_1: ',v
w=str(v)
f.write('Mean dataset_1: ')
f.write(w)
f.write("\n")

v=scipy.mean(dataset_2)
print 'Mean dataset_2: ',v
w=str(v)
f.write('Mean dataset_2: ')
f.write(w)
f.write("\n")

v=scipy.mean(dataset_3)
print 'Mean dataset_3: ',v
w=str(v)
f.write('Mean dataset_3: ')
f.write(w)
f.write("\n")

# ***
v=scipy.var(dataset_1)
print 'Var dataset_1: ',v
w=str(v)
f.write('Var dataset_1: ')
f.write(w)
f.write("\n")

v=scipy.var(dataset_2)
print 'Var dataset_2: ',v
w=str(v)
f.write('Var dataset_2: ')
f.write(w)
f.write("\n")

v=scipy.var(dataset_3)
print 'Var dataset_3: ',v
w=str(v)
f.write('Var dataset_3: ')
f.write(w)
f.write("\n")

# ***
v=scipy.stats.skew(dataset_1)
print 'sesgo dataset_1: ',v
w=str(v)
f.write('sesgo dataset_1: ')
f.write(w)
f.write("\n")

v=scipy.stats.skew(dataset_2)
print 'sesgo dataset_2: ',v
w=str(v)
f.write('sesgo dataset_2: ')
f.write(w)
f.write("\n")

v=scipy.stats.skew(dataset_3)
print 'sesgo dataset_3: ',v
w=str(v)
f.write('sesgo dataset_3: ')
f.write(w)
f.write("\n")

# ***
v=scipy.stats.kurtosis(dataset_1)
print 'curtosis dataset_1: ',v
w=str(v)
f.write('curtosis dataset_1: ')
f.write(w)
f.write("\n")

v=scipy.stats.kurtosis(dataset_2)
print 'curtosis dataset_2: ',v
w=str(v)
f.write('curtosis dataset_2: ')
f.write(w)
f.write("\n")

v=scipy.stats.kurtosis(dataset_3)
print 'curtosis dataset_3: ',v
w=str(v)
f.write('curtosis dataset_3: ')
f.write(w)
f.write("\n")

# ***
v=kstest(dataset_1,'norm')
print 'KS test, Gaussian dataset_1?',v
w=str(v)
f.write('KS test, Gaussian dataset_1? ')
f.write(w)
f.write("\n")

v=kstest(dataset_2,'norm')
print 'KS test, Gaussian dataset_2?',v
w=str(v)
f.write('KS test, Gaussian dataset_2? ')
f.write(w)
f.write("\n")

v=kstest(dataset_3,'norm')
print 'KS test, Gaussian dataset_3?',v
w=str(v)
f.write('KS test, Gaussian dataset_3? ')
f.write(w)
f.write("\n")

f.write("\n")
f.close()
raw_input()


*********************************                        *****************************
Código Programa3:

# Practica: Estadistica Ajuste
#

import numpy
import numpy as np
import scipy.stats
from scipy import stats

a=numpy.random.normal(size=1000)
loc, std=stats.norm.fit(a)
print loc,'\n',std

f=open('E:/Estadistica_Ajuste.txt','a')
f.write(str(loc) + str(std) + '\n')

v=scipy.mean(a)
print 'Mean: ',v
f.write('Mean: ' + str(v) + '\n')

v=scipy.median(a)
print 'Median: ',v
f.write('Median: ' + str(v) + '\n')

v=min(a)
print 'Min: ',v
f.write('Min: ' + str(v) + '\n')

v=max(a)
print 'Max: ',v
f.write('Max: ' + str(v) + '\n')

v=scipy.var(a)
print 'Var: ',v
f.write('Var: ' + str(v) + '\n')

v=scipy.stats.skew(a)
print 'Sesgo: ',v
f.write('Sesgo: ' + str(v) + '\n')

v=scipy.stats.kurtosis(a)
print 'Kurtosis: ',v
f.write('Kurtosis: ' + str(v) + '\n')

v=scipy.stats.scoreatpercentile(a,90)
print 'Percentil 90: ',v
f.write('Percentil 90: ' + str(v) + '\n')

f.write('\n')
f.close()



raw_input()


*********************************                     ***************************





martes, 29 de julio de 2014

Censado de datos. Monitorea un ambiente.

Censado de datos. Monitorea un ambiente.

Categoría: 1. Programación y electrónica.

Este ejercicio es de utilidad para realizar un registro puntual o permanente de las condiciones de temperatura y luminosidad de un ambiente que se maneja automáticamente. De esta manera verificar que las condiciones que se programaron o se ajustaron en los equipos acondicionadores funcionan correctamente. Los datos se registran en un archivo de texto cada 10 minutos. También se despliegan en una pagina web muy simple que se refresca cada cinco segundos.

Una de las ventajas de este programa es que mientras estás visualizando la página web, los datos se refrescan cada 5 segundos (esto puede modificarse en el código) pero si cierras la página, el registro de datos continúa, refrescándose cada 60 segundos por default (también lo puedes programar con otra frecuencia).

El hardware para este ejercicio consiste en lo siguiente:
- Placa de desarrollo Arduino UNO R3.
- Shield Ethernet Arduino.
- Placa para prototipos.
- Tres termorresistencias (termistor) conectadas a GND con una resistencia de 10k_Ohm y calibradas.
- Una fotorresistencia de 2M_Ohm, conectada a GND con una resistencia de 10k_Ohm.
- Un led con resistencia de 1k_Ohm conectada a GND.
- Un buzzer.
- Fuente de alimentación 9Vcc.

En esta aplicación se combina el uso de las funciones de Ethernet y de la micro SD de Arduino Ethernet Shield. Se utilizaron como base los ejemplos DataLogger y WebServer que acompañan al IDE de Arduino. Además se trata de poner este equipo a trabajar de manera indepenciente. Es decir, no necesita estar conectado al puerto USB de la computadora, pero se pueden visualizar los datos en tiempo real en una computadora conectada a la misma red donde se conecta el servidor (Ethernet Shield).

De esta manera, no se depende del monitor serial para el despliegue de los datos censados ni se dedica una conexión USB para alimentar el circuito. 

Como aplicaciones para este sistema se sugiere el monitoreo de temperatura e iluminación durante un periodo de tiempo determinado, 24 a 48 h por ejemplo, y así detectar si la temperatura se mantiene dentro del rango deseado, o si se está fuera de las condiciones durante ciertas horas del día o la noche. También podemos calcular con cierta precisión la hora en que se encienden y apagan las luces. Esto puede ser útil si tenemos un sistema controlado automáticamente, pero queremos tener un registro de que esto está ocurriendo realmente. Podemos poner por ejemplo el caso en que una luminaria quede fuera de servicio. El equipo funciona correctamente, pero la iluminación no se encendió a la hora requerida. 

Esta aplicación se puede llevar a un invernadero donde las plantas necesitan un fotoperiodo mínimo para crecer o para no florear. Un laboratorio de cultivo de tejidos vegetales in vitro, donde se necesita exponer los cultivos a un fotoperiodo mínimo, normalmente 16 horas de iluminación por 8 horas de oscuridad. Una instalación donde se crían peces o aves y que requieren que la temperatura ambiente (aire o agua) estén siempre dentro de rango óptimo. 

En ambos casos, iluminación y temperatura, podemos agregar una alerta sonora cuando una de las condiciones salga de rango.

El sistema se puede manejar de manera permanente, teniendo cuidado de vaciar el archivo de destino cada semana o cada mes. Para no saturar la capacidad de la tarjeta de la memoria micro SD del Shield Arduino.

Este ejemplo se puede ampliar a otro tipo de mediciones, como pH, potencial redox, humedad relativa, presión atmosférica, concentración de CO (monóxido de carbono), entre otros. Contando con los sensores necesarios.

Así mismo es posible aumentar el número de sensores hasta 6 en el caso de ArduinoUNO. Si se trata de censar mayor número e variables se puede usar el Arduino Mega (16 entradas analógicas), que también es compatible con Ethernet Shield.


Gráfico 1. 

Los datos almacenados durante las 24h del día en un archivo de texto pueden procesarse con una hoja de cálculo (Microsoft Excel en este caso) para hacer un análisis visual de un gráfico. En él se puede distinguir claramente las horas de insolación y las horas de oscuridad. Para eso es necesario colocar la hora de inicio del censado, esto debe hacerse manualmente abriendo el archivo de texto y agregando la hora y fecha en el encabezado. En el editor de textos, menú [Editar] [Hora y fecha]. Es recomendable colocar en el encabezado del archivo de texto las ecuaciones de los sensores y la entrada analógica a la que se conectaron A0-A5 (para Arduino UNO).

Las temperaturas interior y en el sistema monitoreado (agua) se mantienen dentro de un rango estrecho, mientras que la temperatura exterior muestra una amplia variación, que durante el día se comporta de manera semejante a la intensidad luminosa (Ampliar el gráfico para ver detalles).

Los sensores de temperatura se encapsularon en un tubo de ensayo (vidrio) con bolitas de plomo unidas con silicón en el fondo (para evitar flotación). El tapón del tubo se selló con silicón para mantener siempre seco el termistor (Imagen 1).

Imagen 1.

La calibración de los termistores se hizo contra un termómetro de mercurio (Ver: calibración de un termistor). Colocando agua con hielo y tomando lecturas analógicas (0 - 1023) y de temperatura simultáneamente, hasta que el agua alcanza la temperatura ambiente. Otra serie de datos se toma en un vaso con agua caliente, hasta que se temperatura es la misma que el ambiente.

A continuación se muestra el código del programa (IDE Arduino).

/**********************************************
DataLogger_&_WebServer

Basado en:
DataLogger
 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe

WebServer
 created 18 Dec 2009
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe

**********************************************/

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

int buzzer = 6;
int redLed = 9;
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,177);  // 192.168.1.177
EthernetServer server(80);
const int chipSelect = 4;
float sensor[5];
long double tiempoActual,tiempoPrevio,tiempoPrevio2;
long double intervalo = 600000;  // 10 minutos
long double intervalo2 = 60000;  // 1 minuto

void setup() {
  Serial.begin(9600);
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());

  Serial.print("Inicializando SD card ...");
  pinMode(10,OUTPUT);
  if (!SD.begin(chipSelect))
  {
    Serial.println("Falla en tarjeta. Inserte tarjeta ...");
    return;
  }
  Serial.println("Se inicalizo tarjeta SD ...");

  pinMode(buzzer,OUTPUT);
  pinMode(redLed,OUTPUT);
}


void loop() {
  tiempoActual=millis();
  EthernetClient client = server.available();

  if (client)
  {
    digitalWrite(redLed,HIGH);
    refreshWebServer();
    digitalWrite(redLed,LOW);
  }

  else
  {
    if (tiempoActual - tiempoPrevio2 > intervalo2)
    {
      digitalWrite(redLed,HIGH);
      refreshOutWeb();
      tiempoPrevio2 = millis();
      digitalWrite(redLed,LOW);
    }
  }

  if (tiempoActual - tiempoPrevio > intervalo)
  {
    digitalWrite(buzzer,HIGH);
    digitalWrite(redLed,HIGH);
    escribeArchivo();
    tiempoPrevio=millis();
    digitalWrite(buzzer,LOW);
    digitalWrite(redLed,LOW);
  }
}

/*********************        *********************/

void escribeArchivo()
{
  String dataString = "";
  int dato;  // Solo acepta int en la funcion String(dato);
  int tiempo = tiempoActual/60000;

  dataString += "\n";
  dataString += String(tiempo);
  dataString += "\t";

  for (int analogPin = 0; analogPin < 4; analogPin++)
  {
    dato = sensor[analogPin];
    dataString += String(dato);
    dataString += "\t";
  }

  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile)
  {
    dataFile.println(dataString);
    dataFile.close();
    Serial.println(dataString);
  }
  else {
    Serial.println("error opening datalog.txt");
  }
}


/*******************         **********************/

void refreshWebServer()
{
  EthernetClient client = server.available();

    digitalWrite(redLed,HIGH);
 
    Serial.println("new client");
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        if (c == '\n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
 client.println("Refresh: 5");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          for (int analogChannel = 0; analogChannel < 4; analogChannel++) {
            float sensorReading = analogRead(analogChannel);
            client.print("analog input ");
            client.print(analogChannel);
            client.print(" is \t");
            if (analogChannel == 0)
            {
              client.print("Interior ");
              sensorReading = 0.1591*sensorReading - 56.113;
              sensor[analogChannel]=sensorReading;
              client.print(sensorReading);
              client.print(" Celsius degree.");
            }
            if (analogChannel == 1)
            {
              client.print("Exterior ");
              sensorReading = 0.1116*sensorReading - 35.358;
              sensor[analogChannel]=sensorReading;
              client.print(sensorReading);
              client.print(" Celsius degree.");
            }
            if (analogChannel == 2)
            {
              client.print("Sistema ");
              sensorReading = 0.13318*sensorReading - 43.9853;
              sensor[analogChannel]=sensorReading;
              client.print(sensorReading);
              client.print(" Celsius degree.");
            }
            if (analogChannel == 3)
            {
              client.print("Iluminacion ");
              sensor[analogChannel]=sensorReading;
              client.print(sensorReading);
              client.print(" Digital value (0-1023).");
            }
            else
            {
              client.println(" ");
            }
            client.println("<br />");    
          }
          client.println("</html>");
          break;
        }
        if (c == '\n') {
         currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
    Serial.println("client disonnected");
 
    digitalWrite(redLed,LOW);
}


/*******************        ***********************/

void refreshOutWeb()
{
          for (int analogChannel = 0; analogChannel < 4; analogChannel++)
          {
            float sensorReading = analogRead(analogChannel);
            if (analogChannel == 0)
            {
              sensorReading = 0.1591*sensorReading - 56.113;
              sensor[analogChannel]=sensorReading;
            }
            if (analogChannel == 1)
            {
              sensorReading = 0.1116*sensorReading - 35.358;
              sensor[analogChannel]=sensorReading;
            }
            if (analogChannel == 2)
            {
              sensorReading = 0.13318*sensorReading - 43.9853;
              sensor[analogChannel]=sensorReading;
            }
            if (analogChannel == 3)
            {
              sensor[analogChannel]=sensorReading;
            }
            else
            {
            }
          }
}

Los datos de calibración de los termómetros se graficaron en Excel y se ajustaron a una recta por el método de regresión por mínimos cuadrados. Las ecuaciones obtenidas son las siguientes:

Termometro1. Temperatura interior:              y=0.1591*x-56.113               Pin: A0
Termometro2. Temperatura exterior:             y=0.1116*x-35.358               Pin: A1
Termometro3. Temperatura del sistema:        y=0.13318*x-43.9853           Pin: A2

La fotorresistencia no se calibró. Para este dispositivo se registran los valores digitales leídos por el convertidor analógico digital (valores de 0 a 1023).

Figura 2.

Para poder visualizar los datos censados, se abre una ventana en el explorador y en la barra de direcciones se escribe la dirección IP que se utiliizó en el código del programa. Los valores se refrescan cada 5 segundos, lo cual es adecuado, ya que por ser una página web tan sencilla casi no consume recursos de la computadora en que se despliegan.

El valor de tiempo registrado en el archivo de texto está en minutos. Para poderlo manejar en Excel se recomienda convertir los datos de esa columna a valores de hora (00:00 a 23:59). Esto se hace fácilmente con la función TIEMPO(hora,minuto,segundo). Como el dato está en minutos, se colocan ceros en hora y segundo, en el atributo minuto se escribe la dirección de la celda que se va a convertir). En la primer celda de la siguiente columna se coloca la hora de inicio y a partir de la segunda se coloca una fórmula que suma esa primera celda (fija, como en $C$7+B8).

En el siguiente enlace puedes encontrar un proyecto para la construcción de un medidor de pulso utilizando optoelectrónica.

Dispositivos electrónicos programables como el que se utilizó en este ejemplo, pueden servir para la automatización de otras aplicaciones. En este enlace puedes ver unas notas sobre automatización de un invernadero. O en forma de presentación (automatización de un invernadero).






viernes, 31 de mayo de 2013

Sensor analógico y señal luminosa con Arduino UNO

Sensor analógico y señal luminosa con Arduino UNO.

Categoría: 1. Programación y electrónica.

Este es un programa sencillo para Arduino UNO, que envía pulsos a un LED cuya duración se ajusta instantáneamente a la intensidad de la iluminación detectada con un sensor (fotorresistencia de 10kOhm) colocado en la entrada analógica A0.
Tres LEDs (puede ser uno o dos si se prefiere) se conectan a los pines digitales I/O 9, 10 y 11 por el ánodo. El cátodo de cada LED se conecta a una resistencia de 220Ohm, conectada a tierra (GND). La fotorresistencia se conecta entre los pines A0 y +5V. En una de las terminales de la fotorresistencia se conecta una resistencia de 10kOhm que se conecta a tierra (GND).
El código es el siguiente:
/*
LedRBG_AnalogRead
*/

#define Blue 9
#define Green 10
#define Red  11

int sensorValue;
unsigned long tiempoTotal, ultimoRegistro, ultimoReporte;
int intervalo;
int reporte = 1500;
byte ultimoEstado = LOW;
byte estadoActual = LOW;

void setup()  {
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  Serial.begin(9600);
}

void loop()  {
  sensorValue = analogRead(A0);
  intervalo = sensorValue / 2;
  tiempoTotal = millis();

  if (tiempoTotal - ultimoRegistro > intervalo)  {
    // digitalWrite(Blue, estadoActual);
    // digitalWrite(Red, estadoActual);
    digitalWrite(Green, estadoActual);
    ultimoRegistro = millis();
    if (estadoActual == LOW)
      estadoActual = HIGH;
    else
      estadoActual = LOW;
  }

  double iluminacion = sensorValue;
  iluminacion = iluminacion * 100 / 1023;

  if (tiempoTotal - ultimoReporte > reporte)  {
    Serial.print("La iluminacion actual es: ");
    Serial.print(iluminacion);
    Serial.println(" %");
    ultimoReporte = millis();  }
}




Figura 1. Aspecto del circuito ya montado.


En el código se utilizó el método de contar el tiempo mediante la función millis(). De esta manera, se pueden controlar muchos procesos independientes unos de otros sin que haya interferencias como sucede con la instrucción delay(ms).
Si se disminuye la iluminación que recibe la fotorresistencia el pulso enviado al LED disminuye su duración y también el tiempo que dura la pausa (LOW), que es de la misma duración.

Bibliografía
Sitio Arduino Homepage: http://www.arduino.cc/







lunes, 22 de octubre de 2012

Diseño de un pulsador con C++

Diseño de un pulsador utilizando el circuito integrado NE555.

Categoría: 1. Programación y electrónica.

Se desarrolló un programa en C++ que calcula la frecuencia, el tiempo del ciclo o periodo (T, en s), el tiempo en HIGH (t1, en s) y el tiempo en LOW (t2, en s). Genera una tabla que se despliega en la consola de Visual C++ y al mismo tiempo genera un archivo, pulsda06.txt, con un formato separado por tabuladores. Este archivo de texto puede abrirse con el programa Excel, de manera que se pueden manejar sus valores para elaborar diferentes tipos de gráficos.
El diagrama del circuito es el siguiente:



Las ecuaciones de diseño según la hoja de datos para este circuito integrado son las siguientes:
t1 = 0.693*(RA+RB)*C
t2 = 0.693*(RB)*C
T = t1+t2 = 0.693*(RA+2RB)*C
f = 1.44 / (RA+2RB)*C
Output driver duty cycle = t1 / (t2+t1) = RB / (RA+2RB)

El programa se corre con un valor inicial de 2200ohm para Ra y termina con 10Mohm con incrementos de 50000ohm en cada ciclo. Para Rb se toma un valor inicial de 2200ohm y se incrementa en 1000ohm en cada ciclo. Se utilizó un capacitor de 2.2uF.
En el archivo de salida la primera columna corresponde al número de ciclo de cálculo, la segunda columna es el valor de Ra (ohm), la tercera columna es el valor de Rb (ohm), la cuarta columna es el valor de t1 (s), la quinta columna es el valor de t2 (s), la sexta columna es el valor de T (s), la séptima columna es el valor de f (s-1) y la octava columna es el valor del ciclo de trabajo (número fraccionario).
El código del programa es el siguiente:



// PulsadorNE555_04.cpp: define el punto de entrada de la aplicación de consola.
//
// PulsadorNE555_03.cpp: archivo de proyecto principal.
// despliega los valores de #corrida, Ra, Rb, t1, t2, T, freq y dutyCycle separados por punto y coma
// se trata que el archivo de texto pueda ser leido por el programa Excel con columnas
// separadas por tabulador

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
using namespace std;

int main() {
 float t1;
 float t2;
 float Time;
 float freq;
 float dutyCycle;
 float Ra = 2200;
 float Rb = 2200;
 float c = 0.0000022;
 int caso = 1;
 ofstream out("D:\\pulsda06.txt");
 while (Ra < 10000000)  {
 t1 = 0.693*(Ra + Rb)*c;
 t2 = 0.693*Rb*c;
 Time = t1+t2;
 freq = 1/Time;
 dutyCycle = (Ra + Rb) / (Ra + 2*Rb);

 cout << caso;
 cout << ";\t";
 cout << Ra;
 cout << ";\t";
 cout << Rb;
 cout << ";\t";
 cout << t1;
 cout << ";\t";
 cout << t2;
 cout << ";\t";
 cout << Time;
 cout << ";\t";
 cout << freq;
 cout << ";\t";
 cout << dutyCycle;
 cout << ";\n";
 // escribeArchivo:
 if (!out)  {
  cout << "cannot open file...\n";
  return 1; }
 out << caso;
 out << "\t";
 out << Ra;
 out << "\t";
 out << Rb;
 out << "\t";
 out << t1;
 out << "\t";
 out << t2;
 out << "\t";
 out << Time;
 out << "\t";
 out << freq;
 out << "\t";
 out << dutyCycle;
 out << "\n";
 Ra = Ra + 50000;
 Rb = Rb + 1000;
 caso++;  }
 out.close();
 system("pause");
 return 0; }

Para este ejemplo se registran 200 ciclos de cálculo que quedan guardados en el archivo pulsda06.txt. Este archivo se abrió desde Excel donde se generaron tres gráficos que se muestran a continuación:


 Gráfico 1. Valores de t1 y t2 en función del valor de Ra
 Gráfico 2. Valor de la frecuencia en función de Ra
Gráfico 3. Valor del ciclo de trabajo en función de Ra

Con los datos generados se puede obtener para un ciclo de 1.04s, un ciclo de trabajo de 0.978, con duración de t1 de 1.018s y t2 de 0.022s. Para ello se requiere valores de Ra= 652200ohm y para Rb= 15200ohm. El valor de f será de 0.96 s^-1.

Por lo pronto es todo. Saludos y hasta la próxima.

Referencias bibliográficas:
2010. Texas Instruments. NA555, NE555, SA555, SE555. Data Sheet. USA.
1998. STMicroelectronics NE555, SA555, SE555. Data Sheet. USA.
2004. Applied digital circuits: 555 timer, analog to digital (ADC) and digital to analog (DAC) conversion (DAC). University of California at Berkeley. Physics 111 Laboratory. Basic semiconductor circuits. Lab 11 & 12. USA.
http://www.bgsu.edu/departments/compsci/docs/write.html








jueves, 6 de septiembre de 2012

Manejo de la micro SD del Shield Ethernet

Manejo de la micro SD del Shield Ethernet.

Categoría: 1. Programación y electrónica.

Se desarrolló un programa (C/C++) para tener juntas algunas instrucciones útiles en el manejo de los archivos contenidos en la tarjeta micro SD del Shield Ethernet de Arduino. Con base en algunos programas de ejemplo como dumpFile, cardInfo y readWrite se desarrolló un menú y las acciones leerArchivo, leerMicroSD y borrarArchivo se programaron como funciones. El monitor serial despliega el menú de opciones y el usuario introduce a través del mismo monitor serial la opción que desea que el programa ejecute. Se verificó cada una de las tres opciones del menú por separado, una vez que crrieron correctamente al cargarlas en la tarjeta Arduino se procedió a colocarlas en un programa único una a una, y teniendo cuidado de no sobrepasar el límite de almacenamiento de la tarjeta (32256 bytes). Para terminar, se verificó el programa completo (16598 bytes) y se cargó en la tarjeta electrónica.

El código es el siguiente:

#include <SD.h>
Sd2Card card;
SdVolume volume;
SdFile root;
const int chipSelect = 4;
void setup ()  {
  Serial.begin(9600);
  pinMode(10, OUTPUT);
  Serial.println("enter:\n 1. cardInfo\n 2. leerArchivo\n 3. borrarArchivo\n 0. Salir ");  }
 
void loop()  {
  if (Serial.available() > 0)  {
    char ch = Serial.read();
    if (ch == '1')  {
       cardInfo(); }
    else if (ch == '2')  {
       leerArchivo(); }
    else if (ch == '3')  {
       borrarArchivo();  }
    else if (ch == '4')  {  }
    else if (ch == '0')  {  }  }  }

void cardInfo ()  {
  Serial.print("\ninitializing SD card... ");
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("* is a card is inserted?");
    Serial.println("* Is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    return;  }
    else {
   Serial.println("Wiring is correct and a card is present.");  }
  Serial.print("\nCard type: ");
  switch(card.type()) {
    case SD_CARD_TYPE_SD1:
      Serial.println("SD1");
      break;
    case SD_CARD_TYPE_SD2:
      Serial.println("SD2");
      break;
    case SD_CARD_TYPE_SDHC:
      Serial.println("SDHC");
      break;
    default:
      Serial.println("Unknown");  }
  if (!volume.init(card)) {
    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    return;  }
  uint32_t volumesize;
  Serial.print("\nVolume type is FAT");
  Serial.println(volume.fatType(), DEC);
  Serial.println();
 
  volumesize = volume.blocksPerCluster();   
  volumesize *= volume.clusterCount();      
  volumesize *= 512;                        
  Serial.print("Volume size (bytes): ");
  Serial.println(volumesize);
  Serial.print("Volume size (Kbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);
  Serial.print("Volume size (Mbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);
 
  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);
 
  root.ls(LS_R | LS_DATE | LS_SIZE);  }


void borrarArchivo ()  {
  Serial.print("Initializing SD card...");
  if (!SD.begin(chipSelect))  {
    Serial.println("initialization failed!...");
    return;  }
  Serial.println("initialization done!...");
 
  if (SD.exists("pulsedat.txt"))  {
    Serial.println("pulsedat.txt exists...");  }
  else  {
    Serial.println("pulsedat.txt doesn't exist...");  }
  Serial.println("removing pulsedat.txt...");
  SD.remove("pulsedat.txt");
 
  if (SD.exists("pulsedat.txt"))  {
    Serial.println("pulsedat.txt exists...");  }
  else  {
    Serial.println("pulsedat.txt doesn't exist..");  }  }


void leerArchivo ()  {
  Serial.print("Initializing SD card...");
    if (!SD.begin(chipSelect))  {
      Serial.println("initialization failed!...");
      return;  }
  Serial.println("initialization done!...");
 
  File dataFile = SD.open("pulsedat.txt", FILE_READ);
  if (dataFile)  {
    Serial.println("pulsedat.txt exists...");
    while (dataFile.available())  {
      Serial.write(dataFile.read());  }
      dataFile.close();  }
  else  {
    Serial.println("error opening pulsedat.txt...");  }  }
     
De esta manera, se puede trabajar de manera más amigable con el programa presentado anteriormente como EjercicioPulseDat04 para el registro de pulsaciones generadas por un multivibrador astable construido con un circuito integrado NE555 (o NE556).

Es todo por ahora. Saludos.

La bibliografía contenida en la entrada 'registro de datos a una micro SD' también aplica para esta entrada, y es la siguiente:
Bibliografía.

Stengel, Lester. 2006. El ABC de la electrónica. Steren Electrónica SA de CV. México.
Arduino. Sitio web: http://www.arduino.cc/. Consulta el 31 de julio de 2012.







sábado, 1 de septiembre de 2012

Registro de datos a una micro SD

Registro de un generador de pulsos a un archivo de texto (pulsedat.txt).

Categoría: 1. Programación y electrónica.

El programa generador de pulsos funciona registrando los momentos en que un generador de pulsos, un botón pulsador conectado a una resistencia de 10K o, como en este ejercicio, un circuito electrónico generador de pulsos. Este circuito está basado en un circuito integrado NE555 o NE556.

El circuito astable es como sigue:



El programa EjercicioPulseDat04 lee la salida digital del vibrador astable (pin 3 en el NE555 o pines 5 o 9 del NE556), registra en el monitor serial los tiempos de encendido (HIGH) y de apagado (LOW) utilizando la instrucción millis(); y también registra el número de pulsos acumulado. De esta manera es posible medir el tiempo de duración en HIGH y el tiempo de duración en LOW.
Estos mismos datos se registran en un archivo llamado pulsedat.txt, almacenado en la tarjeta de datos micro SD contenida en el Shield Ethernet de Arduino. Como se sabe, esta tarjeta va acoplada con la tarjeta electrónica Arduino UNO.

Las siguientes imágenes muestran la salida en el monitor serial y la ventana del ambiente Arduino con parte del programa a la vista.





El programa se desarrolló tomando como base el ejemplo 'state change detection' (edge detection) que proorciona el ambiente Arduino para la tarjeta electrónica Arduino UNO. También se utilizó parte del código del ejemplo 'datalogger' para la tarjeta Ethernet Shield.

/*
EjercicioPulseDat04
*/
/*
  State change detection (edge detection)

 Often, you don't need to know the state of a digital input all the time,
 but you just need to know when the input changes from one state to another.
 For example, you want to know when a button goes from OFF to ON.  This is called
 state change detection, or edge detection.

 This example shows how to detect when a button or button changes from off to on
 and on to off.

 The circuit:
 * pushbutton attached to pin 2 from +5V
 * 10K resistor attached to pin 2 from ground
 * LED attached from pin 13 to ground (or use the built-in LED on
   most Arduino boards)

 created  27 Sep 2005
 modified 30 Aug 2011
 by Tom Igoe
This example code is in the public domain.

 http://arduino.cc/en/Tutorial/ButtonStateChange

 */
#include <SD.h>
const int  buttonPin = 2;   
const int ledPin = 13;      
int buttonPushCounter = 0; 
int buttonState = 0;       
int lastButtonState = 0; 
File dataFile;
void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  pinMode(10, OUTPUT);
 
    if (!SD.begin(4))  {
    Serial.println("card failed, or not present...");
    return;  }
  Serial.println("card initialized...");
 
  Serial.println("removing pulsedat.txt...");
  SD.remove("pulsedat.txt");
  if (SD.exists("pulsedat.txt"))  {
    Serial.println("pulsedat.txt exists...");  }
  else  {
    Serial.println("pulsedat.txt doesn't exist...");  }  }

void loop()
{
  buttonState = digitalRead(buttonPin);
  if (buttonState != lastButtonState)
  {
    if (buttonState == HIGH)
    {
      buttonPushCounter++;
      Serial.print("number of button pushes:  ");
      Serial.println(buttonPushCounter);
      Serial.print("time on= ");
      Serial.println(millis());
 
  dataFile = SD.open("pulsedat.txt", FILE_WRITE);
  if (dataFile) 
      {
    Serial.println("writing to pulsedat.txt...");
    dataFile.print("number of button pushes:  ");
    dataFile.println(buttonPushCounter);
    dataFile.print("time on= ");
    dataFile.println(millis());   
    dataFile.close();
    Serial.println("action done..."); 
      }
  else 
      {
    Serial.println("error opening pulsedat.txt"); 
      }   
    }
 
   
    else
      {
      Serial.print("time off= ");
      Serial.println(millis());
     
  dataFile = SD.open("pulsedat.txt", FILE_WRITE);
  if (dataFile) 
        {
    Serial.println("writing to pulsedat.txt...");
    dataFile.print("time off= ");
    dataFile.println(millis());
   
    dataFile.close();
    Serial.println("action done..."); 
        }
  else 
        {
    Serial.println("error opening pulsedat.txt"); 
        }
      }
 
  lastButtonState = buttonState;
  if (buttonPushCounter % 4 == 0)
      {
    digitalWrite(ledPin, HIGH);
      }
  else
      {
   digitalWrite(ledPin, LOW);
      }
  }
}
Este programa con algunas modificaciones puede adaptarse para medir variables de uno o más sensores (temperatura, humedad, presión atmosférica, intensidad lumínica, voltaje, etc) conectados a las entradas analógicas de la tarjeta electrónica.

Por lo pronto es todo. Saludos.

Bibliografía.

Stengel, Lester. 2006. El ABC de la electrónica. Steren Electrónica SA de CV. México.
Arduino. Sitio web: http://www.arduino.cc/. Consulta el 31 de julio de 2012.