Sensor de radiación ultravioleta con Arduino https://polaridad.es/sensor-radiacion-ultravioleta-arduino-indice-uv-uvm30a-guva-s12sd/
por Víctor Víctor Ventura | publicado en: Portada en: Portada | 36 Radiación ultravioleta (UV)
Además de la radiación la radiación electromagnética que los humanos somos capaces de ver, existen rangos con longitudes de onda más larga, infrarrojo, y más corta, ultravioleta. corta, ultravioleta. Una parte de esta radiación, la de longitud más corta, tiene efectos ionizantes, efectos ionizantes, es decir, es capaz de desplazar electrones de los átomos modificando su estado. Mientras que la zona visible se encuentra aproximadamente en el rango comprendido entre los 400 nm y los 700 nm, la ultravioleta la ultravioleta comienza en los 10 nm (ultravioleta extremo) y termina en los 400 nm (UVA, onda larga) l arga) donde comienza la parte visible del espectro.
La La radiación ultravioleta se utiliza para diversos fines prácticos, desde matar bacterias hasta revelar las placas de los circuitos impresos que fabricas en casa pero además de útil puede ser también peligrosa para la salud. La capa La capa de ozono que rodea la tierra nos defiende de buena parte de la radiación ultravioleta más ionizante de la que nos llega desde el sol: la de longitud de longitud de onda más corta, los «rayos UVC», que se encuentran entre los 100 nm y 280 nm. Pero aún queda la UVB, entre 280 nm y 315 nm, también en parte ionizante y la UVB, entre 315 nm y 400 nm, que es menos agresiva. agresiva. Precisamente es la radiación la radiación ultravioleta la que produce la capa la capa de ozono en la estratosfera al disociar las moléculas de oxígeno O 2 en dos átomos O, muy reactivos, que forman el ozono O 3 al reaccionar cada uno con otra molécula de O 2. Índice UV (ultravioleta) El índice El índice UV es un sistema estándar sencillo para medir la radiación ultravioleta que llega desde el sol a la tierra que sirve como orientación para determinar el riesgo potencial para la salud. La La Organización Mundial de la Salud publica una una guía práctica sobre el índice UV en la que explica los riesgos para la salud de la la radiación ultravioleta y propone algunas medidas de protección en función de su intensidad.
Esta guía práctica sobre el Índice Solar Mundial es una recomendación conjunta de la Organización la Organización Mundial de la Salud, la Organización la Organización Meteorológica Mundial, el Programa el Programa de las Naciones Unidas para el Medio Ambiente y la la Comisión Internacional de Protección contra la Radiación no Ionizante. Para hacer su uso más sencillo establece un código de colores que asocia a los diferentes niveles: verde para el nivel bajo (índices 1 y 2), amarillo para el moderado (índices del 3 al 5), naranja para el alto (índices 6 y 7), rojo para el nivel muy alto (índices del 8 al 10) y morado para el nivel extremadamente alto (índices 11 en adelante) En función de estos índices y del fototipo de cada persona recomienda unos tiempos máximos de exposición y unas recomendaciones preventivas. Medida de la radiación ultravioleta y cálculo del índice UV
Para medir con precisión la radiación la radiación ultravioleta, ultravioleta, no solo la que llega del sol, también la que producen ciertos equipos, como las lámparas o LED con este tipo de luz, se utilizan espectrorradiómetros. Calibrados con los anteriores dispositivos, se pueden usar otros más sencillos (y económicos) como fotodiodos, que suelen ser de tipo Schottky. El «truco» consiste en medir la luz de cierta longitud de onda (del entorno de los UVA, normalmente) y presumir que corresponde, más o menos proporcionalmente, con la irradiancia ultravioleta; irradiancia ultravioleta; puede puede parecer poco preciso, pero es razonablemente funcional como para estimar el UVi. Existen componentes que incorporan sólo la función de medida de la radiación ultravioleta o, más exactamente la del índice del índice UV, UV, como el GUVA-S12SD, que incorporan amplificación, como el ML8511, ambos analógicos, o digitales, como el VEML6070, que comunican por I²C por I²C el resultado de la medición. También están presentes como una función secundaria en algunos sensores de iluminación y proximidad, como el Si1145, que también t ambién utiliza el protocolo I²C. Ninguno de los anteriores componentes es especialmente caro ni adquirido en series pequeñas y existen versiones de todos ellos en módulos que se pueden usar para prototipar o para realizar pruebas. Para usarlo como ejemplo en este artículo he elegido el GUVA-S12SD que es muy popular (seguramente tienes uno en tu insoladora ultravioleta de circuitos) y que se puede encontrar en módulos UVM30A que pueden usarse fácilmente con Arduino. El rango de longitudes de onda que mide el GUVA-S12SD se sitúa entre los 240 nm y los (aproximadamente) 380 nm, muy adecuado para determinar la radiación la radiación ultravioleta más peligrosa.
Como la fórmula para calcular el índice UV, que puede consultarse en la guía práctica sobre el índice UV es muy compleja como para actualizar el resultado frecuentemente con un microcontrolador, el fabricante del módulo proporciona una tabla con los valores de salida con los que se alcanzan los diferentes niveles del índice UV.
Según puede verse en la gráfica anterior, especialmente para radiaciones bajas, el comportamiento no es completamente lineal aunque a efectos de establecer el índice UV, con unos márgenes relativamente grandes, no se cometería un gran error al considerarlo como lineal para ahorrar unos bytes de memoria o simplificar la programación. En cualquier caso, en los programas de ejemplo para Arduino que he desarrollado como ejemplo se usa la tabla de valores del fabricante. Usar el fotodiodo Schottky GUVA-S12SD del módulo UVM30A con Arduino
Usar el módulo UVM30A no puede ser más sencillo, basta con alimentarlo y leer el voltaje con el que representa el nivel de radiación ultravioleta que detecta. Puede alimentarse con tensiones entre 3 V y 5 V y puede entregar a la salida entre 0 y 1200 mV (aunque de hecho no supere el voltio). Como por encima de 1100 mV de salida corresponde a un índice UV extremadamente alto (un índice mayor que 10) se puede usar la referencia interna de 1100 mV para distribuir mejor la
sensibilidad aunque renunciando a la posibilidad de determinar cuánto se supera el índice 10, sólo estimando que se alcanzado el 11, pero ahorrando un divisor de tensión para usar como referencia de entrada analógica en Arduino.
Para trabajar con la referencia interna de 1100 mV en las placas Arduino basadas en el microcontrolador ATmega168 o en el ATmega328 (como Arduino Uno) se usa analogReference(INTERNAL) y se usa analogReference(INTERNAL1V1) para las placas Arduino Mega. El tiempo de lectura del GUVA-S12SD es bastante rápido y la respuesta razonablemente estable. En los montajes de prueba, la lectura analógica que se realiza del módulo UVM30A desde Arduino, seguramente por la disposición de los cables, no es tan buena como la que da el osciloscopio, aunque resulta más que aceptable para medir el índice UV y es de esperar que en un prototipo montado en un circuito impreso incluso mejore. En cualquier caso, para tratar de eliminar las desviaciones de posibles interferencias, el programa realiza varias mediciones, tantas como sea posible en cierto periodo de tiempo, y las promedia para establecer un valor que compara con los de la tabla del fabricante para calcular el índice UV.
Puesto que la lectura analógica con analogRead() tiene una resolución de 10 bits (excepto en Arduino Due y en Arduino Zero, en los que puede configurarse a 12 bits) para obtener la tensión entregada por el módulo UVM30A hay que convertir el rango de 0 a 1023, que devuelve la función, al rango que va de 0 a 1100, que son los milivoltios que se han establecido como referencia con analogReference(). Para realizar la conversión hay que multiplicar por 1100 y dividir por 1023. En el primer programa de ejemplo se usa como una constante para hacerlo en una única operación y facilitar la lectura. El siguiente programa de ejemplo lee el sensor de radiación ultravioleta GUVA-S12SD del módulo UVM30A cada cierto intervalo ESPERA_ENTRE_LECTURAS y va calculando la media de las lecturas que muestra cada cierto tiempo ESPERA_ENTRE_PRESENTACIONES junto con el valor correspondiente en mV
#define PIN_GUVAS12SD A0 // Pin al que se conecta el módulo con el UVM30A #define ESPERA_ENTRE_LECTURAS 100 // Leer cada 100 ms. Necesita unos 100 µs para cada lectura analógica #define ESPERA_ENTRE_PRESENTACIONES 60000 // Mostrar la lectura cada minuto #define COEFICIENTE_VOLTAJE 1.07526881720430107527 // 1100/1023 1.07526881720430107527 La lectura máxima es de 1023 que corresponde a 1100 mV
1 2 3 4 5 6 7 unsigned int lectura_sensor; 8 unsigned int contador_lecturas=1; 9 float total_lecturas=0.0; 10 float media_lecturas; 11 long cronometro_lecturas; 12 long cronometro_presentaciones; 13 long tiempo_transcurrido; 14 15 void setup() 16 { 17 analogReference(INTERNAL); // Referencia interna de 1100 mV El GUVA-S12SD mide de 0 18 a 1170 mV que corresponde con el índice UV 11 19 //pinMode(PIN_GUVAS12SD,INPUT); // La lectura analógica no necesita inicialización 20 Serial.begin(9600); 21 #if defined (__AVR_ATmega32U4__) 22 while(!Serial);// Esperar a Arduino Leonardo 23 #endif 24 lectura_sensor=analogRead(PIN_GUVAS12SD); // La primera lectura es incorrecta 25 (normalmente cero) y necesita unos 100 µs para cada lectura analógica 26 cronometro_lecturas=millis(); // Esperar un ciclo de lectura para estabilizar el sensor y la 27 entrada analógica 28 cronometro_presentaciones=millis(); 29 } 30 void loop() 31 { 32 tiempo_transcurrido=millis()-cronometro_lecturas; 33 if(tiempo_transcurrido>ESPERA_ENTRE_LECTURAS) 34 { 35 cronometro_lecturas=millis(); 36 lectura_sensor=analogRead(PIN_GUVAS12SD); 37 total_lecturas+=lectura_sensor; 38 media_lecturas=total_lecturas/contador_lecturas++; 39 } 40 tiempo_transcurrido=millis()-cronometro_presentaciones; 41 if(tiempo_transcurrido>ESPERA_ENTRE_PRESENTACIONES) 42 { 43 cronometro_presentaciones=millis(); 44 Serial.print("Lectura sensor: "+String(media_lecturas,DEC)); 45 Serial.print(" (media de "+String(contador_lecturas,DEC)+")"); 46 Serial.print(" Tensión (mV): "+String(media_lecturas*COEFICIENTE_VOLTAJE,DEC)+"\n\n"); contador_lecturas=1; ≃
total_lecturas=0.0; } } Del mismo modo, para ahorrar un poco de proceso se pueden calcular los valores compensados para la tabla de índices. En lugar de comparar la lectura analógica con el valor de la hoja de datos del UVM30A se puede crear un vector que contenga los datos convertidos al rango de 0 a 1023 realizando la operación inversa a la descrita antes. Ya no será necesario calcular la tensión a cada lectura puesto que la tabla de índices está modificada.
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 33 8 39 40 41
#define PIN_GUVAS12SD A0 // Pin al que se conecta el módulo con el UVM30A #define ESPERA_ENTRE_LECTURAS 100 // Leer cada 100 ms #define ESPERA_ENTRE_PRESENTACIONES 30000 // Mostrar el índice cada 30 s #define CANTIDAD_INDICES_UV 11 unsigned int lectura_sensor; unsigned int contador_lecturas=1; float total_lecturas=0.0; float media_lecturas; int valor_indice_uv[CANTIDAD_INDICES_UV]={210,295,378,467,563,646,738,818,907,1003,10 22}; // De 1 a 11 byte indice; boolean buscando_indice_uv; long cronometro_lecturas; long cronometro_presentaciones; long tiempo_transcurrido; void setup() { analogReference(INTERNAL); // Referencia interna de 1100 mV El GUVA-S12SD mide de 0 a 1170 mV que corresponde con el índice UV 11 //pinMode(PIN_GUVAS12SD,INPUT); // La lectura analógica no necesita inicialización Serial.begin(9600); #if defined (__AVR_ATmega32U4__) while(!Serial);// Esperar a Arduino Leonardo #endif lectura_sensor=analogRead(PIN_GUVAS12SD); // La primera lectura es incorrecta (normalmente cero) cronometro_lecturas=millis(); // Esperar un ciclo de lectura para estabilizar el sensor y la entrada analógica cronometro_presentaciones=millis(); } void loop() { tiempo_transcurrido=millis()-cronometro_lecturas; if(tiempo_transcurrido>ESPERA_ENTRE_LECTURAS) { cronometro_lecturas=millis(); lectura_sensor=analogRead(PIN_GUVAS12SD); total_lecturas+=lectura_sensor;
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
media_lecturas=total_lecturas/contador_lecturas++; } tiempo_transcurrido=millis()-cronometro_presentaciones; if(tiempo_transcurrido>ESPERA_ENTRE_PRESENTACIONES) { cronometro_presentaciones=millis(); buscando_indice_uv=true; indice=CANTIDAD_INDICES_UV; while(buscando_indice_uv&&indice>0) { indice--; if(media_lecturas>valor_indice_uv[indice]) { buscando_indice_uv=false; } } Serial.print("Lectura sensor: "+String(media_lecturas,DEC)); Serial.print(" (media de "+String(contador_lecturas,DEC)+")"); Serial.print(" Índice UV: "+String(indice,DEC)); contador_lecturas=1; total_lecturas=0.0; } }
Implementar el método en un semáforo de índice UV
Utilizando el código de colores que publica la Organización Mundial de la Salud en la guía práctica sobre el índice UV se puede construir un sencillo montaje basado en Arduino (que puede ser muy portátil si se usa una placa Arduino Mini, Arduino Micro, Arduino Nano…) que encienda el LED del color correspondiente al índice UV detectado por el GUVA-S12SD de un módulo UVM30A.
En el diagrama se muestra la conexión usada para el código del ejemplo del « solmáforo». El valor de las resistencias, rotulado como 220 Ω en el esquema, se sustituirá por el que corresponda a la tensión a la que deban trabajar los LED (dependiendo del propio LED y de la relación entre
luminosidad y vida útil deseada, seguramente entre 100 Ω y 330 Ω). No es especialmente importante utilizar ni la misma entrada analógica A0 ni las mismas salidas digitales, pero hay que recordar sustituirlas en el programa.
1 #define PIN_VERDE 2 2 #define PIN_AMARILLO 4 3 #define PIN_NARANJA 7 4 #define PIN_ROJO 8 5 #define PIN_MORADO 12 6 #define PIN_GUVAS12SD A0 // Pin al que se conecta el módulo con el UVM30A 7 #define ESPERA_ENTRE_LECTURAS 100 // Tomar una lectura del sensor de ultravioleta cada 8 100 ms 9 #define ESPERA_ENTRE_PRESENTACIONES 10000 // Cambiar el color del semáforo cada 10 1 segundos 0 #define CANTIDAD_INDICES_UV 11 1 #define CANTIDAD_COLORES_UV 5 1 1 unsigned int lectura_sensor; 2 unsigned int contador_lecturas=1; 1 float total_lecturas=0.0; 3 float media_lecturas; 1 int 4 valor_indice_uv[CANTIDAD_INDICES_UV]={210,295,378,467,563,646,738,818,907,1003,1022} 1 ; // De 1 a 11 5 byte 1 color[CANTIDAD_INDICES_UV+1]={PIN_VERDE,PIN_VERDE,PIN_VERDE,PIN_AMARILLO,PI 6 N_AMARILLO,PIN_AMARILLO,PIN_NARANJA,PIN_NARANJA,PIN_ROJO,PIN_ROJO,PIN_RO 1 JO,PIN_MORADO}; // De 0 a 11 7 byte 1 pin_color[CANTIDAD_COLORES_UV]={PIN_VERDE,PIN_AMARILLO,PIN_NARANJA,PIN_RO 8 JO,PIN_MORADO}; 1 byte indice; 9 byte indice_anterior=0; 2 boolean buscando_indice_uv; 0 long cronometro_lecturas; 2 long cronometro_presentaciones; 1 long tiempo_transcurrido; 2 2 void setup() 2{ 3 analogReference(INTERNAL); // Referencia interna de 1100 mV El GUVA-S12SD mide de 0 a 2 1170 mV que corresponde con el índice UV 11 4 //pinMode(PIN_GUVAS12SD,INPUT); // La lectura analógica no necesita inicialización 2 for(indice=0;indice
8 (normalmente cero) 2 cronometro_lecturas=millis(); // Esperar un ciclo de lectura para estabilizar el sensor y la 9 entrada analógica 3 cronometro_presentaciones=millis(); 0} 3 void loop() 1{ 3 tiempo_transcurrido=millis()-cronometro_lecturas; 2 if(tiempo_transcurrido>ESPERA_ENTRE_LECTURAS) 3 { 3 cronometro_lecturas=millis(); 3 lectura_sensor=analogRead(PIN_GUVAS12SD); 4 total_lecturas+=lectura_sensor; 3 media_lecturas=total_lecturas/contador_lecturas++; 5 } 3 tiempo_transcurrido=millis()-cronometro_presentaciones; 6 if(tiempo_transcurrido>ESPERA_ENTRE_PRESENTACIONES) 3 { 7 cronometro_presentaciones=millis(); 3 buscando_indice_uv=true; 8 indice=CANTIDAD_INDICES_UV; 3 while(buscando_indice_uv&&indice>0) 9 { 4 indice--; 0 if(media_lecturas>valor_indice_uv[indice]) 4 { 1 buscando_indice_uv=false; 4 } 4 } if(indice_anterior!=indice) { digitalWrite(color[indice_anterior],LOW); digitalWrite(color[indice],HIGH); indice_anterior=indice; } contador_lecturas=1; total_lecturas=0.0; } } Para la versión portátil se puede usar una batería LiPo y un cargador para USB (el módulo más a la derecha de la imagen). Yo he utilizado dos interruptores, uno para cargar la batería y otro para encender el dispositivo, pero es posible hacerlo solamente con uno y hacer una u otra operación en función de la posición en la que conmute; será necesario usar conmutadores, no interruptores, claro. Además de poner el sensor a 90° o mejor en el lado contrario a los LED (el semáforo sirve para el exterior y se puede usar en condiciones de mucha luz) puede ser útil añadir a la caja un resalte que haga de visera para distinguir mejor el color.
Si usas una placa Arduino Uno se puede alimentar con una pila de 9 V (recuerda que la tensión de salida de Arduino determinará el cálculo de las resistencias de los LED). Otra alternativa es usar un único LED RGB que establezca el color en función del índice UV. Seguro que habrá ocasión de tratar estas alternativas en próximos artículos ¡no te los pierdas! Arduino, GUVA-S12SD, Índice UV, Módulo, U
#define PIN_GUVAS12SD A0 // Pin al que se conecta el módulo con el UVM30A #define ESPERA_ENTRE_LECTURAS 100 // Leer cada 100 ms. Necesita unos 100 µs para cada lectura analógica #define ESPERA_ENTRE_PRESENTACIONES 60000 // Mostrar la lectura cada minuto #define COEFICIENTE_VOLTAJE 1.07526881720430107527 // 1100/1023 1.07526881720430107527 La lectura máxima es de 1023 que corresponde a 1100 mV ≃
unsigned int lectura_sensor; unsigned int contador_lecturas=1; float total_lecturas=0.0; float media_lecturas; long cronometro_lecturas; long cronometro_presentaciones; long tiempo_transcurrido; void setup() { analogReference(INTERNAL); // Referencia interna de 1100 mV El GUVA-S12SD mide de 0 a 1170 mV que corresponde con el índice UV 11 //pinMode(PIN_GUVAS12SD,INPUT); // La lectura analógica no necesita inicialización Serial.begin(9600); #if defined (__AVR_ATmega32U4__) while(!Serial);// Esperar a Arduino Leonardo #endif lectura_sensor=analogRead(PIN_GUVAS12SD); // La primera lectura es incorrecta (normalmente cero) y necesita unos 100 µs para cada lectura analógica cronometro_lecturas=millis(); // Esperar un ciclo de lectura para estabilizar el sensor y la entrada analógica cronometro_presentaciones=millis(); } void loop() { tiempo_transcurrido=millis()-cronometro_lecturas;
if(tiempo_transcurrido>ESPERA_ENTRE_LECTURAS) { cronometro_lecturas=millis(); lectura_sensor=analogRead(PIN_GUVAS12SD); total_lecturas+=lectura_sensor; media_lecturas=total_lecturas/contador_lecturas++; } tiempo_transcurrido=millis()-cronometro_presentaciones; if(tiempo_transcurrido>ESPERA_ENTRE_PRESENTACIONES) { cronometro_presentaciones=millis(); Serial.print("Lectura sensor: "+String(media_lecturas,DEC)); Serial.print(" (media de "+String(contador_lecturas,DEC)+")"); Serial.print(" Tensión (mV): "+String(media_lecturas*COEFICIENTE_VOLTAJE,DEC)+"\n\n"); contador_lecturas=1; total_lecturas=0.0; } }
1 #define PIN_GUVAS12SD A0 // Pin al que se conecta el módulo con el UVM30A 2 #define ESPERA_ENTRE_LECTURAS 100 // Leer cada 100 ms 3 #define ESPERA_ENTRE_PRESENTACIONES 30000 // Mostrar el índice cada 30 s 4 #define CANTIDAD_INDICES_UV 11 5 6 unsigned int lectura_sensor; 7 unsigned int contador_lecturas=1; 8 float total_lecturas=0.0; 9 float media_lecturas; 10 int valor_indice_uv[CANTIDAD_INDICES_UV]={210,295,378,467,563,646,738,818,907,1003,1022}; // De 1 a 11 11 byte indice; 12 boolean buscando_indice_uv; 13 long cronometro_lecturas; 14 long cronometro_presentaciones; 15 long tiempo_transcurrido; 16 17 void setup() 18 { 19 analogReference(INTERNAL); // Referencia interna de 1100 mV El GUVA-S12SD mide de 0 a 1170 mV que corresponde con el índice 20 UV 11 21 //pinMode(PIN_GUVAS12SD,INPUT); // La lectura analógica no necesita inicialización 22 Serial.begin(9600); 23 #if defined (__AVR_ATmega32U4__) 24 while(!Serial);// Esperar a Arduino Leonardo 25 #endif 26 lectura_sensor=analogRead(PIN_GUVAS12SD); // La primera lectura es incorrecta (normalmente cero) 27 cronometro_lecturas=millis(); // Esperar un ciclo de lectura para estabilizar el sensor y la entrada analógica 28 cronometro_presentaciones=millis(); 29 } 30 void loop() 31 { 32 tiempo_transcurrido=millis()-cronometro_lecturas; 33 if(tiempo_transcurrido>ESPERA_ENTRE_LECTURAS) 34 { 35 cronometro_lecturas=millis(); 36 lectura_sensor=analogRead(PIN_GUVAS12SD); 37 total_lecturas+=lectura_sensor; 38 media_lecturas=total_lecturas/contador_lecturas++; 39 } 40 tiempo_transcurrido=millis()-cronometro_presentaciones; 41 if(tiempo_transcurrido>ESPERA_ENTRE_PRESENTACIONES) 42 { 43 cronometro_presentaciones=millis(); 44 buscando_indice_uv=true; 45 indice=CANTIDAD_INDICES_UV;
46 while(buscando_indice_uv&&indice>0) 47 { 48 indice--; 49 if(media_lecturas>valor_indice_uv[indice]) 50 { 51 buscando_indice_uv=false; 52 } 53 } 54 Serial.print("Lectura sensor: "+String(media_lecturas,DEC)); 55 Serial.print(" (media de "+String(contador_lecturas,DEC)+")"); 56 Serial.print(" Índice UV: "+String(indice,DEC)); 57 contador_lecturas=1; 58 total_lecturas=0.0; 59 } }
#define PIN_VERDE 2 #define PIN_AMARILLO 4 #define PIN_NARANJA 7 #define PIN_ROJO 8 #define PIN_MORADO 12 #define PIN_GUVAS12SD A0 // Pin al que se conecta el módulo con el UVM30A #define ESPERA_ENTRE_LECTURAS 100 // Tomar una lectura del sensor de ultravioleta cada 100 ms #define ESPERA_ENTRE_PRESENTACIONES 10000 // Cambiar el color del semáforo cada 10 segundos #define CANTIDAD_INDICES_UV 11 #define CANTIDAD_COLORES_UV 5 unsigned int lectura_sensor; unsigned int contador_lecturas=1; float total_lecturas=0.0; float media_lecturas; int valor_indice_uv[CANTIDAD_INDICES_UV]={210,295,378,467,563,646,738,818,907,1003,1022}; // De 1 a 11 byte color[CANTIDAD_INDICES_UV+1]={PIN_VERDE,PIN_VERDE,PIN_VERDE,PIN_AMARILLO,PIN _AMARILLO,PIN_AMARILLO,PIN_NARANJA,PIN_NARANJA,PIN_ROJO,PIN_ROJO,PIN_ROJO ,PIN_MORADO}; // De 0 a 11 byte pin_color[CANTIDAD_COLORES_UV]={PIN_VERDE,PIN_AMARILLO,PIN_NARANJA,PIN_ROJ O,PIN_MORADO}; byte indice; byte indice_anterior=0; boolean buscando_indice_uv; long cronometro_lecturas; long cronometro_presentaciones; long tiempo_transcurrido; void setup() { analogReference(INTERNAL); // Referencia interna de 1100 mV El GUVA-S12SD mide de 0 a 1170 mV que corresponde con el índice UV 11 //pinMode(PIN_GUVAS12SD,INPUT); // La lectura analógica no necesita inicialización
for(indice=0;indice
ESPERA_ENTRE_LECTURAS) { cronometro_lecturas=millis(); lectura_sensor=analogRead(PIN_GUVAS12SD); total_lecturas+=lectura_sensor; media_lecturas=total_lecturas/contador_lecturas++; } tiempo_transcurrido=millis()-cronometro_presentaciones; if(tiempo_transcurrido>ESPERA_ENTRE_PRESENTACIONES) { cronometro_presentaciones=millis(); buscando_indice_uv=true; indice=CANTIDAD_INDICES_UV; while(buscando_indice_uv&&indice>0) { indice--; if(media_lecturas>valor_indice_uv[indice]) { buscando_indice_uv=false; } } if(indice_anterior!=indice) { digitalWrite(color[indice_anterior],LOW); digitalWrite(color[indice],HIGH); indice_anterior=indice; } contador_lecturas=1; total_lecturas=0.0; } }
He preparado un sencillo programa que graba cada 4 minutos los valores que se han ido promediando al leer el sensor de ultravioleta.
Además del lector SD utiliza un conmutador que cambia entre GND y nivel alto (3 V o 5 V, según qué placa uses) para activar y desactivar la grabación (y así no tener errores al crear-cerrar el documento) y un LED para indicar que se están grabando datos. Lógicamente, debes usar los números de pin de los #define para las conexiones o cambiarlos según tu hardware. Una posible mejora incluiría un reloj en tiempo real, como el RTC DS3231. Puedes encontrar algún artículo en el blog sobre cómo gestionar el DS3231 desde Arduino usando las comunicaciones I2C, así como una librería para manejar la fecha y la hora usando un RTCDS3231 con Arduino. Como el código está muy comentado, no te aburro con más charla. Espero que te sirva. ¡Vuelve pronto por polaridad.es!
#define MODO_PRUEBAS // Borrar esta definición para que no se envíen a la consola datos de informe en modo prueba #define PREFIJO_ARCHIVO "UVI" // Los nombres de los documentos tienen el formato UVIn.CSV siendo n el número (correlativo) de archivo #define EXTENSION_ARCHIVO ".CSV" // Se usa el formato CSV, valores expresados como texto y separados por comas (también puede usarse punto y coma, espacio, tabulador…)
#define SEPARADOR_CSV "," // El separador usado para el formato CSV #define PIN_GUVAS12SD A0 // Pin al que se conecta el módulo con el UVM30A #define PIN_CS_SD 4 // Pin usado para activar (CS/SS) la tarjeta #define PIN_CS_CONVENCIONAL 10 // Pin usado normalmente para activar dispositivos SPI #define PIN_INFORME_ACTIVO 8 // LED que indica que la tarjeta SD está preparada para grabar datos #define PIN_CONMUTADOR_SD 9 // Pin que le indica al programa que debe preparar la tarjeta SD y grabar datos (nivel alto) o que debe vaciar el buffer y dejar de usar la tarjeta SD (nivel bajo) #define ESPERA_REINTENTO_ERROR 3000 // Esperar unos segundos antes de reintentar después de detectar un error #define ESPERA_MUESTREO_UVI 240000 // Intervalo entre grabaciones del valor del UVi (4 minutos a petición de Juan 4*60*1000 ms) #define COEFICIENTE_VOLTAJE 1.07526881720430107527 // 1100/1023 1.07526881720430107527 La lectura máxima es de 1023 que corresponde a 1100 mV ≃
#include // La librería SD estándar de Arduino File informe_uvi; char nombre_archivo[13]; // buffer para el nombre del documento con el informe UVi char buffer_numero_archivo[4]; // buffer con el número (sufijo) del documento
unsigned int numero_archivo=0; // Número de archivo que se busca para, si no existe, usarlo como sufijo del nombre boolean informe_activo=false; // No se están grabando datos en la tarjeta SD boolean nuevo_estado_informe; // Lectura actual del conmutador de la tarjeta SD unsigned long cronometro; // Tiempo entre grabaciones del índice UVi en la tarjeta SD unsigned long contador_lecturas; // Número de lecturas tomadas entre grabaciones (para calcular el valor intermedio) unsigned int lectura_sensor; // Valor entregado por el sensor GUVAS12SD float total_lecturas=0.0; // Suma de los valores muestreados en una sesión float media_lecturas; // Valor intermedio de los valores muestreados entre grabaciones String registro_csv; // Datos que se graban en una línea del documento CSV (milisegundos (desde encendido) ,valor calculado, lecturas promediadas) void setup() { #ifdef MODO_PRUEBAS Serial.begin(9600); // Preparar el puerto serie #ifdef __AVR_ATmega32U4__ while(!Serial);// Esperar a Arduino Leonardo #endif #endif analogReference(INTERNAL); // Referencia interna de 1100 mV El GUVA-S12SD mide de 0 a 1170 mV que corresponde con el índice UV 11 lectura_sensor=analogRead(PIN_GUVAS12SD); // La primera lectura es incorrecta (normalmente cero) pinMode(PIN_CS_SD,OUTPUT); // El pin al que realmente se conecta la tarjeta SD debe ser de salida para activarla (nivel bajo) o desactivarla (nivel alto) digitalWrite(PIN_CS_SD,HIGH); // Desactivar la tarjeta SD pinMode(PIN_CS_CONVENCIONAL,OUTPUT); // Puede que sea necesario usar como salida (o simplemente reservar) el pin que normalmente se utiliza para SPI por seguridad (si el verdadero no es el 10) digitalWrite(PIN_CS_SD,HIGH); // Desactivar el dispositivo en el bus SPI habitual while(!SD.begin(PIN_CS_SD)) // Reintentar hasta activar el lector de tarjetas SD (si no se consigue, no hay razón para seguir el programa) { #ifdef MODO_PRUEBAS Serial.print("Error al inicializar el lector de tarjetas SD. Se reintenta en " ); Serial.print(ESPERA_REINTENTO_ERROR); Serial.println(" ms."); #endif delay(ESPERA_REINTENTO_ERROR); // Esperar antes de reintentar } #ifdef MODO_PRUEBAS Serial.println("Lector de tarjetas SD inicializado"); #endif pinMode(PIN_INFORME_ACTIVO,OUTPUT); // Pin del LED de aviso de SD activada digitalWrite(PIN_INFORME_ACTIVO,LOW); // Apagar el LED que informa de que se pueden estar grabando datos en la tarjeta pinMode(PIN_CONMUTADOR_SD,INPUT); } void loop() { nuevo_estado_informe=digitalRead(PIN_CONMUTADOR_SD); if(nuevo_estado_informe!=informe_activo) // Si ha cambiado el estado (ha cambiado la posición
del conmutador) { if(nuevo_estado_informe) // Si el conmutador está en modo grabar (y antes no lo estaba) { do { itoa(numero_archivo++,buffer_numero_archivo,10); strcpy(nombre_archivo,PREFIJO_ARCHIVO); strcat(nombre_archivo,buffer_numero_archivo); strcat(nombre_archivo,EXTENSION_ARCHIVO); #ifdef MODO_PRUEBAS Serial.println("Probando a usar el documento "+String(nombre_archivo)); #endif } while(SD.exists(nombre_archivo)); informe_uvi=SD.open(nombre_archivo,FILE_WRITE); if(informe_uvi) // Si se ha conseguido preparar el documento…
{ informe_activo=true; // …cambiar el estado a activo
digitalWrite(PIN_INFORME_ACTIVO,HIGH); // Encender el LED para informar de que se pueden estar grabando datos en la tarjeta SD contador_lecturas=0; // Empezar a contar lecturas entre grabaciones (para calcular el valor intermedio) #ifdef MODO_PRUEBAS Serial.println("Informe UVi activo"); #endif cronometro=millis(); // (re)Iniciar el cronómetro para saber si ha pasado el tiempo entre grabaciones } else { #ifdef MODO_PRUEBAS Serial.println("Se ha producido un error al tratar de crear el documento."); Serial.print("Se reintentará dentro de "); Serial.print(ESPERA_REINTENTO_ERROR); Serial.println(" ms."); #endif delay(ESPERA_REINTENTO_ERROR); // Esperar antes de reintentar } } else // Si el conmutador está en modo NO grabar (y antes no lo estaba) { informe_uvi.flush(); // Vaciar el buffer informe_uvi.close(); // Cerrar el archivo informe_activo=false; // Cambiar el estado a inactivo (no se graban datos en la tarjeta SD) digitalWrite(PIN_INFORME_ACTIVO,LOW); // Apagar el LED que informa de grabación activa #ifdef MODO_PRUEBAS Serial.println("Informe UVi desactivado"); #endif } } if(informe_activo) // Si la grabación está activa { if((unsigned long)(millis()-cronometro)>ESPERA_MUESTREO_UVI) // Si la grabación está
activa y ha pasado el tiempo entre grabaciones sucesivas…
{ cronometro=millis(); // (re)Iniciar el cronómetro para saber si ha pasado el tiempo entre grabaciones media_lecturas*=COEFICIENTE_VOLTAJE; // Corregir los valores tomados para que trabaje con el voltaje (más o menos coincide con un UVi decimal) #ifdef MODO_PRUEBAS Serial.print("Guardado el valor "); Serial.print(media_lecturas); Serial.print(" en "); Serial.print(cronometro); Serial.print(" ms ("); Serial.print(contador_lecturas); Serial.println(" muestras)"); #endif registro_csv=String(cronometro,DEC); // Componer la línea CSV (registro) con el tiempo, registro_csv+=String(SEPARADOR_CSV); // un separador, registro_csv+=String(media_lecturas,DEC); // Valor intermedio de las lecturas hasta el momento, registro_csv+=String(SEPARADOR_CSV); // otro separador registro_csv+=String(contador_lecturas,DEC); // y el número de muestras tomadas para calcular el valor intermedio (más de 1100000 en un Arduino Uno con intervalos de 4 minutos) informe_uvi.println(registro_csv); // Grabar la línea CSV (el registro) contador_lecturas=0; // Empezar a contar lecturas entre grabaciones (para calcular el nuevo valor intermedio) } else // Si no toca grabar, tomar una medida y actualizar el cálculo del valor intermedio { lectura_sensor=analogRead(PIN_GUVAS12SD); // leer el sensor contador_lecturas++; // incrementar el número de lecturas realizadas total_lecturas+=lectura_sensor; media_lecturas=total_lecturas/contador_lecturas; } } } Si quieres hacer el montaje del artículo, básicamente necesitas los componentes de la foto, un lector de tarjetas SD para el programa del ejemplo y un reloj en tiempo real para la «mejora» (almacenar la fecha y la hora de la medida)
El sensor de ultravioleta. Mejor un módulo, que incluye junto al sensor otros compoentes pasivos que puedas necesitar. Una placa (compatible) Arduino. Mejor pequeña. 5 LED de los colores correspondientes (mejor todos teñidos o todos transparentes, pero con luz de color) 5 resistencias que correspondan a la tensión y corriente de salida de la placa Arduinoelegida y de la intensidad de luz deseada (alta, que es para exteriores) Entre 100 Ω y 330 Ω y un cuarto de vatio. Menor tensión menor resistencia, mayor iluminación menor resistencia
Una batería. Te recomiendo una de las de teléfono móvil que entregan 3,7 V (seguro que tienes alguna muerta de risa en un cajón) Un cargador de batería. Dependiendo de la alimentación de la placa Arduino, puedes necesitar también aumentar la tensión entregada por la batería, en tal caso te recomiendo que uses un módulo de los que incluyen ambas cosas (y además protegen la batería) que cuesta igual (más o menos) y minimiza el montaje. Te recomiendo un par de interruptores o conmutadores para el encendido y la carga, aunque puedes hacerlo con un conmutador de dos posiciones (tres terminales, uno común que el conmutador conecta con uno u otro de los restantes según cambie su posición) Si quieres hacer el montaje del ejemplo del comentario necesitas añadir un módulo lector de tarjetas SD para Arduino (mejor MicroSD) y la correspondiente tarjeta. Si optas por un módulo (es mi consejo para ti) incluirá los componentes que necesite para conectar directamente a la placa Arduino (debes buscar que la alimentación de uno y otro sean compatibles). Por si necesitas más información que la del ejemplo, en el blog hay un artículo que explica cómo almacenar datos desde Arduino hasta una tarjeta SD Si quieres añadir un RTC, necesitarás el módulo correspondiente. Te recomiendo el reloj en tiempo real DS3231, que suele incluir espacio para su propia batería que durará más que la del montaje. En el blog hay varios artículos sobre cómo controlar desde Arduino el RTCDS3231
Si no te ves muy suelto con esto, te aconsejo que pidas ayuda en una tienda de electrónica cercana. No sé en tu país, mi experiencia en España es que están muy preparados y encantados de ayudar; suele ser una ocasión para aprender un montón. Son pocos componentes, pedirlos fuera te va a ahorrar poco y un pedido al extranjero en agosto (siempre refiriéndome a España) puede significar que llegue a final de septiembre.
Como parece que hay muchos lectores interesados, os he preparado un ejemplo de cómo se podría mostrar el índice UV usando un servidor web basado en Arduino con una conexión Ethernet. Después conectar todo y alimentarlo (mostrar también la consola serie es de ayuda para monitorizar el funcionamiento), hay que usar un navegador web en un PC que esté en la misma red que la conexión Ethernet de Arduino y escribir en la barra de direcciones 192.168.1.252 Si la red no es la del ejemplo ( 192.168.1.X) habrá que cambiar el valor de la variable ip. Espero que os dé una idea para hacer vuestro proyecto ¡Suerte!
1 #define PIN_GUVAS12SD A0 // Pin al que se conecta el módulo con el UVM30A 2 #define CANTIDAD_INDICES_UV 11 3 String color[CANTIDAD_INDICES_UV+1]={"1C0","1C0","1C0","FE0","FE0","FE0","FA0","FA0","F11","F11","F11","E1B"}; // 4 Colores de los índices para el fondo de la página web 5 6 #include 7 #include 8 9 byte mac[]={0x12,0x34,0x56,0x78,0x9A,0xBC}; // Dirección MAC inventada 10 IPAddress ip(192,168,1,252); // Dirección IP arbitraria en el rango 192.168.1. 11 EthernetServer servidor_web(80); // El puerto por defecto del HTTP es el 80 12 EthernetClient cliente_web; 13 char lectura_ethernet; 14 char lectura_anterior; 15 byte indice; 16 17 void setup() 18 { 19 Serial.begin(115200); 20 while(!Serial); 21 Ethernet.begin(mac,ip); 22 servidor_web.begin(); 23 Serial.println(F("Servidor web iniciado")); 24 analogReference(INTERNAL); // Referencia interna de 1100 mV El GUVA-S12SD mide de 0 a 1170 mV que corresponde con el índice 25 UV 11 26 analogRead(PIN_GUVAS12SD); // La primera lectura es incorrecta (normalmente cero) 27 } 28 29 void loop() 30 { 31 cliente_web=servidor_web.available(); 32 if(cliente_web) 33 { 34 lectura_anterior=0; // Cualquier carácter que no sea \n para evitar malinterpretar el final del texto 35 Serial.println(F("Se ha conectado un cliente")); // Monitorizar que el cuando se conecta el cliente (el navegador web) 36 Serial.println(F("Datos enviados por el cliente:\n")); // Monitorizar la información que envía el cliente (el navegador web) 37 while(cliente_web.connected()) // Cuando el cliente (el navegador web) se conecte 38 { 39 indice=indice_uv(); // Leer el sensor 40 if(cliente_web.available()) // Si llegan datos desde el cliente (el navegador web) 41 { 42 lectura_ethernet=cliente_web.read(); // Leer una letra del texto enviado por el cliente 43 Serial.print(lectura_ethernet); // Mostrar la letra recibida del cliente para monitorizar el funcionamiento 44 if(lectura_ethernet=='\r'&&lectura_anterior=='\n') // Si llega CR y antes había llegado LF se interpreta como una línea en blanco 45 { 46 cliente_web.flush(); // Como la línea en blanco marca el final de la petición HTTP ignorar el resto del texto que mande el cliente 47 cliente_web.println(F("HTTP/1.1 200 OK")); // Cabecera de petición aceptada correctamente 48 cliente_web.println(F("Content-Type: text/html")); // Cabecera de tipo de respuesta enviada (texto en formato HTML) 49 cliente_web.println(F("Connection: close")); // Al terminar, cerrar la conexión para liberar al servidor lo antes posible 50 cliente_web.println(); // Una línea en blanco para indicar el final de las cabeceras y el comienzo del código HMTML 51 cliente_web.print(F("")); // Es un documento HTML 52 cliente_web.print(F("")); // El texto del HTML está en español 53 cliente_web.print(F("")); 54 cliente_web.print(F("<meta charset=\"utf-8\">")); // La codificación del texto es UTF-8 (importante para los caracteres no-ASCII 55 como las tildes) 56 cliente_web.print(F("<meta name=\"viewport\" content=\"width=device-width,height=device-height,initial-scale=1.0,user57 scalable=no,minimum-scale=1.0,maximum-scale=1.0\">")); // ventana para que se muestre al tamaño correspondiente en un dispositivo 58 móvil y la escala sea fija 59 cliente_web.print(F("Índice UV")); // Título de la página (para la ventana del navegador) 60 cliente_web.print(F("")); 61 cliente_web.print(F("")); 65 cliente_web.print(F("
68 cliente_web.print(F("height:100%;")); 69 cliente_web.print(F("\">")); 70 cliente_web.print(F("")); 76 cliente_web.print(F("El índice UV es ")); 77 cliente_web.print(indice); // Valor numérico del índice UV 78 cliente_web.print(F("
")); 79 cliente_web.print(F("")); 80 cliente_web.print(F("")); 81 cliente_web.print(F("")); 82 cliente_web.stop(); // Cerrar la conexión para liberar al servidor 83 } 84 lectura_anterior=lectura_ethernet; // Recordar la lectura anterior para detectar una línea en blanco y saber cuándo termina la petición 85 HTTP 86 } 87 } 88 Serial.println(F("\nEl cliente se ha desconectado")); // Avisar de que se ha cerrado la conexión para monitorizarla 89 } 90 } 91 92 byte indice_uv() 93 { 94 static const int valor_indice_uv[CANTIDAD_INDICES_UV]={210,295,378,467,563,646,738,818,907,1003,1022}; // De 1 a 11 95 unsigned int lectura_sensor=analogRead(PIN_GUVAS12SD); 96 boolean buscando_indice_uv=true; 97 byte indice=CANTIDAD_INDICES_UV; 98 while(buscando_indice_uv&&indice>0) 99 { 100 indice--; 101 if(lectura_sensor>valor_indice_uv[indice]) { buscando_indice_uv=false; } } return indice; }