Propuesta de mejora de código sin MEF proyecto
Sensor UV
Código para el Arduino
La
siguiente tabla es el código de Arduino que propongo, sin Máquina de Estados
Finitos, explicado paso a paso. En principio podrían copiar cada parte en orden
y probarlo en el Arduino.
Primero propongo unos truquitos
del lenguaje C++ |
//
Trucos de C++ #define
ENTER "\n" template<class
CualquierTipo> inline Print &operator
<< (Print &izq, CualquierTipo der ) {
izq.print( der ) ;
return izq ; } |
A continuación,
incluir las librerías necesarias. Hasta el momento, incluyeron ustedes el de
DHT (Detector de Humedad y Temperatura Relativa DHT22) y la pantallita LCD
16x2 mediante Protocolo I2C. Para
“conversar” en dicho protocolo se necesita la librería Wire.h nativa de
Arduino C++. |
//
Incluir librerías
#include <DHT.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h> |
Seguido,
definimos los pines de los componentes que agregamos al Arduino. |
//
Definición de pines
int
_pinDHT = 3
, _verd1 = 4
, _verd2 = 5
, _amar1 = 6
, _amar2 = 7
, _rojo1 = 8
, _rojo2 = 9
, _pinUV = A0
; |
Es momento de
instanciar los componentes no sencillos: la pantalla LCD (salida, actuador) y
el sensor DHT (entrada, sensor). |
// Creación
de variables
LiquidCrystal_I2C
_pantallita(
0x27 // Dirección estándar del dispositivo I2C para el LCD
, 16 // Letras de ancho del LCD
, 2 // Renglones del LCD
)
;
DHT
_sensorHT( _pinDHT, DHT22 )
; |
Ahora
crearemos las variables de las mediciones, más una que nos va a ayudar con el tiempo: |
float
_medidaSensorUV = 0.0f
, _medidaHumedad
, _medidaTemperatura
, _medidaIndiceDeCalor
, _tiempo = 0
; |
Inicializamos
todo dentro del setup(): |
void
setup() {
Serial.begin(9600);
Serial << "arduino:conectado" << ENTER ;
pinMode( _verd1, OUTPUT ) ;
pinMode( _verd2, OUTPUT ) ;
pinMode( _amar1, OUTPUT ) ;
pinMode( _amar2, OUTPUT ) ;
pinMode( _rojo1, OUTPUT ) ;
pinMode( _rojo2, OUTPUT ) ;
_pantallita.init() ;
_pantallita.backlight();
_sensorHT.begin(); } |
Programamos
el loop() |
void
loop() { |
Y verificamos cada 2 segundos el sensor de humedad DHT, que es
solicita este sensor para no tomar mediciones erróneas. En todo caso,
podríamos realizarlo también cada 3, 4 ó 5 segundos. |
if( _tiempo >= 2000 ) { |
Intentamos extraer del sensor las medidas de humedad y temperatura, y
corroboramos que fue posible y que no generó un valor no-numérico o NaN
(Not-A-Number) con la función isnan() de Arduino. |
_medidaHumedad = _sensorHT.readHumidity() ; _medidaTemperatura = _sensorHT.readTemperature() ; if( isnan( _medidaHumedad ) || isnan( _medidaTemperatura ) ) {
Serial << "error:" << "Error
obteniendo los datos del sensor DHT11" << ENTER ; _tiempo = 0 ; return; } |
Si lo hemos logrado, podemos quizás computar el Índice de calor, que
depende de la temperatura y la humedad, y la librería de DHT tiene una
función que lo calcula. Esto es opcional. |
_medidaIndiceDeCalor = _sensorHT.computeHeatIndex( _medidaTemperatura , _medidaHumedad ) ; |
Es momento de probar el sensor de Rayos UltraVioleta. Medimos
directamente por la función analogRead() |
_medidaSensorUV = analogRead( _pinUV ) ; _medidaSensorUV = map( _medidaSensorUV , 0, 1023 , 0, 100 ); Serial << "Sensor UV:" <<
_medidaSensorUV << ENTER ; |
Aquí me pareció óptimo crear una función. Habían muchos renglones
parecidos, para activar las luces correspondientes. Así que pensé en crear
una función y directamente pasarle 1 (=HIGH) y 0 (=LOW) a cada luz que
ustedes plantearon. La función la llamé fnLuces() y pide para cada luz (2xVerdes,
2xAmarillas, 2xRojos) un HIGH o LOW. Con una toma de decisiones según los
números que plantearon, y con ayuda del else, las condiciones se pudieron
cambiar de la siguiente manera: |
if ( _medidaSensorUV < 10 )
fnLuces( 1,0, 0,0, 0,0 ) ; else if( _medidaSensorUV < 25 ) fnLuces( 1,1,
0,0, 0,0 ) ; else if( _medidaSensorUV < 40 ) fnLuces( 1,1,
1,0, 0,0 ) ; else if( _medidaSensorUV < 55 ) fnLuces( 1,1,
1,1, 0,0 ) ; else if( _medidaSensorUV < 70 ) fnLuces( 1,1,
1,1, 1,0 ) ; else fnLuces( 1,1, 1,1, 1,1 ) ; |
Después de analizar los Serial.print() (Serial <<) que
servirían para la AppWeb que necesito para DAII, me acordé que necesitarían
grabar esto en una base de datos, así que junté todos los valores en un solo
print: |
Serial << "humedad:" <<
_medidaHumedad << "%" << ENTER << "temperatura:" <<
_medidaTemperatura << "ºC" << ENTER << "indice de calor:"
<< _medidaIndiceDeCalor << "ºC" << ENTER
<< "grabar:" << _medidaHumedad << "\t" <<
_medidaTemperatura << "\t" <<
_medidaSensorUV << ENTER ; |
Finalizamos el if() de cada 2 segundos, reseteando el tiempo para
volver a medir los próximos 2 segundos. |
_tiempo = 0 ; return ; } // Fin if() de cada 2 segundos |
Mostramos en la pantalla LCD los valores más importantes, y
aumentamos el tiempo transcurrido en 200 milisegundos; la pantalla se
actualiza 5 veces por segundo (1000/200). |
_pantallita.clear() ; _pantallita.print( "Temp: " ) ; _pantallita.print( (int)_medidaTemperatura ) ; _pantallita.print( "C" ) ; _pantallita.print( "Humedad: " ) ; _pantallita.print( (int)_medidaHumedad ) ; _pantallita.print( "%" ) ; delay( 200 ) ; _tiempo = _tiempo + 200 ; } // Fin loop() |
Finalmente creó la función para encender las luces… |
void fnLuces( int v1, int v2, int a1, int a2, int r1, int r2 ) { digitalWrite( _verd1, v1 ) ; digitalWrite( _verd2, v1 ) ; digitalWrite( _amar1, v1 ) ; digitalWrite( _amar2, v1 ) ; digitalWrite( _rojo1, v1 ) ; digitalWrite( _rojo2, v1 ) ; } |
Análisis del resultado del
Monitor Serial
Qué cosas se puede mostrar en el Monitor Serial
si probáramos el Arduino. Ubico primero todos los Serial <<, y agendo
posibilidades. De todos ellos, la App Web necesitará aquellos que comienzan con
una o varias palabras, haya en el medio un signo dos-puntos (:) y termine con
un ENTER (Serial.println).
"humedad:50.04%"
"temperatura:20.50ºC"
"indice de calor:5.33ºC"
"Sensor UV:45.00"
"grabar:50.04\t20.50\t45.00" "error:Error obteniendo los datos del
sensor DHT11" |
Detalles para la App Web
1. Instalar la librería de
WebComponents en Castellano, en el HEAD |
<script
src="https://gorosito.red/componentes" ></script> |
2. En el BODY insertar un componente
Arduino-USB |
<arduino-usb
id="idArduino" eventos="_listaEventosArduino" > </arduino-usb> |
3. En el BODY, luego, crear un SCRIPT
y codificar lo siguiente: |
var
_listaEventosArduino = { humedad:
fnHumedad ,
temperatura: fnTemperatura ,
"indice de calor": fnIndiceCalor ,
"Sensor UV": fnSensorUV , grabar:
fnGrabarEnBD , error: fnErrorAlMedir }; |
La variable
_listaEventosArduino es una lista nombrada (nombre técnico en JavaScript:
objeto, en Python: diccionario, en PHP: array asociativo, en Pascal: record,
en C/C++: Struct). Noten que
tiene que ver con lo del Serial.println(), el signo dos puntos. Como norma
general, en JavaScript, lo que va a la izquierda de cada 2 puntos, puede
tener comillas o no; pero si es más de una palabra, las tiene que tener sí o
sí. |
Ahora creamos
las funciones mencionadas en esa lista: |
<script>
function fnHumedad( _generadaHumedad ) {
idHumedad.innerHTML = _generadaHumedad ; // "50.04%"
idInputHumedad.value = + _generadaHumedad ; // 50.04
console.log( "Humedad leída:", _generadaHumedad ) ;
}
function fnTemperatura( _generadaTemperatura ) {
idTemperatura.innerHTML = _generadaTemperatura ; //
"20.50ºC"
idInputTemp.value = + _generadaTemperatura ; // 20.50
console.log( "Temperatura leída:",
_generadaTemperatura ) ;
}
function fnIndiceCalor( _generadoIndice ) {
// ...
}
function fnSensorUV( _generadoUV ) {
idUV.innerHTML = _generadoUV + "%" ; //
"45%"
idInputUV.value = +_generadoUV ; // 45
idProgresoUV.value = +_generadoUV ;
console.log( "Factor UV medido:", _generadoUV ) ;
} function fnErrorAlMedir( _generadoTexto )
{ console.error( _generadoTexto ) ; } |
Ahora, la
función más importante, la que grabará en una base de datos. Eso lo haremos
mediante la función fetch() de JavaScript y sus métodos .then() y .catch(). La función
fetch() pedirá a un recurso de un servidor web llamado “grabar.php” y le
pasará datos por método GET (acuérdense esto de 4to año, directo en la URL). Los métodos
.then() y .catch() sirven para esperar una respuesta por parte del servidor.
Según esta respuesta, ejecutaremos una función u otra. Veamos cómo
sigue: |
function fnGrabarEnBD( _textoParaGrabar ) {
var _split = _textoParaGrabar.split( "\t" ) ;
var _hum = _split[0] ;
var _tem = _split[1] ;
var _uv = _split[2] ;
fetch(
"/grabar.php?nmHum="+_hum+"&nmTem="+_tem+"&nmUV="+_uv
)
.then( x => x.text() )
.then( fnGrabadoOk )
.catch( fnEnviarError )
;
}
function fnGrabadoOk( _textoGenerado ) { if( _textoGenerado == "1" ) { console.log( "Grabado OK" )
; } else { console.log( "Error
al grabar", _textoGenerado ) ; }
}
function fnEnviarError( _textoGenerado ) {
console.log( "Error al enviar", _textoGenerado ) ;
} |
Acá ya
podemos cerrar el SCRIPT |
</script> |
Endpoint o recurso del
servidor
Recordemos que un Servidor PHP funciona
ofreciendo como recursos archivos guardados en una carpeta pública. Y no sólo
eso, si ese archivo que se pide es un archivo de extensión .php, antes de
enviar información a quien lo pide (por ejemplo, un navegador web, o una
función fetch()), procesará información e imprimirá lo que corresponda (por
esto se dice que PHP permite generar páginas web dinámicamente).
Una vez recordado esto, en un proyecto de
Glitch, basado en basededatos2024, remixarlo con el nombre pedido en el
Classroom, y en la carpeta “publica/” crear el archivo “grabar.php”
(publica/grabar.php).
Código del archivo
publica/grabar.php En primer lugar,
para simplificar el trabajo con una base de datos, existe un archivo llamado
útiles.php que se encuentra en la carpeta base. Lo incluimos: |
<?php include "../utiles.php" ; |
Ahora crearemos una variable llamada $base que se conectará a un
archivo de base de datos tipo SQLite. (Ver más adelante en este documento
cómo crear la base de datos). |
$base = basededatos( "proyectoarduino.db" ) ; |
Ahora consideramos los datos que enviaría el Fetch. Suponiendo lo
siguiente: fetch( “/grabar.php?nmHum=50.04&nmTem=20.50&nmUV=85” ) Habremos enviado por método GET los datos en los name de tres
imaginarios INPUT que son nmHum, nmTem y nmUV… |
$humedad = $get->nmHum ; $temperatura = $get->nmTem ; $uv = $get->nmUV ; $ahora = date( "Y-m-d h:i:s" ) ; |
A continuación creamos la regla SQL de tipo INSERT INTO para agregar
esos datos a la base de datos: |
$reglaSQL = " INSERT INTO TablaMediciones ( _id, _hora, _humedad, _temperatura, _uv ) VALUES ( NULL, '$ahora', '$humedad', '$temperatura', '$uv'
) ; "; $accion = $base->insertar( $reglaSQL ) ; |
Finalmente verificamos si hubo algún tipo de error y mostramos un
cero y el texto del error; pero si está todo OK, imprimiremos un “1”, el que
adoptamos en la función fnGrabadoOk() de la página web… |
if( $accion->error ) { print "0-" . $accion->error ; } else { print "1" ; } ?> |
Crear la base de datos
1. Hacerle vista previa al proyecto. |
2. Ubicar el botón Gestor, que nos
llevará al archivo gestor.php |
3. Te va a pedir una contraseña. Es
la escrita en el archivo “clave.php”. Ahí dice que la contraseña es “admin”
(sin comillas). Ustedes luego la cambiarán. |
4. Ahora te pedirá el nombre de un
archivo. Te va a listar todos los archivos de base de datos existentes, pero
no estará el de “proyectoarduino.db” que citamos más arriba. Escribilo
(obviamente sin comillas) y al presionar el botón, el sistema lo creará. |
5. Vamos a crear la tabla. Escribí el
siguiente comando CREATE TABLE… |
CREATE
TABLE TablaMediciones (
_id INTEGER PRIMARY KEY AUTOINCREMENT
, _hora DATETIME
, _humedad REAL
, _tempertura REAL
, _uv REAL
) |
6.
Ponele un punto y coma. Pero no dejés espacios
luego porque generará un error. Quizás lo mejor para hacer esto es ubicar el
botón “Poner punto y coma”; automáticamente pondrá el Punto y Coma donde
corresponda. |
7.
Para asegurarte que la tabla se creó escribí
“tablas” + Botón Proceder (sin comillas), esto mostrará todas las tablas
creadas en la base de datos. |
8.
Otra cosa que podés hacer es describir la
tabla creada. Con “describir TablaMediciones”, te hará un informe sobre esa
tabla, qué columnas tiene y de qué tipo es cada una. |
n
Comentarios
Publicar un comentario