Esquema Raspberry

Código Python del proyecto.

Mapa de Ficheros

Página-1 Conector dinámico.63 Conector dinámico.87 Conector dinámico Conector dinámico.37 Conector dinámico.41 Conector dinámico.42 Conector dinámico.43 Conector dinámico.46 Conector dinámico.47 writefile.py writefile.py Conector dinámico.52 fotoresistor.py fotoresistor.py funCpu.py funCpu.py Conector dinámico.64 obtenserial.py obtenserial.py Conector dinámico.70 Conector dinámico.71 Conector dinámico.73 Conector dinámico.77 Conector dinámico.81 Conector dinámico.82 Conector dinámico.83 Conector dinámico.84 Conector dinámico.85 Conector dinámico.86 Conector dinámico.88 Conector dinámico.89 Conector dinámico.90 Conector dinámico.91 Main.py Main.py Sen_humedad.py sen_humedad.py welcome.py welcome.py presionATM.py presionATM.py UV.py UV.py muestra.py muestra.py pulsador.py pulsador.py convert_ard_TO_ras.py convert_ard_TO_ras.py Conector dinámico.99 updatorSFTP.py updatorSFTP.py Variables.dat variables.dat Lluvia.py lluvia.py Voltimetro.py voltimetro.py
Si haces click sobre el icono, podrás consultar los pines de la GPIO en todo momento.

Explicación por ficheros

Fichero Principal

Nombre del fichero: Main.py

Librería utilizadas:

#Modulos Sistema
import threading
import time

# Modulos ropios
import sen_humedad
import fotoresistor
import UV
import updatorSFTP
import funCpu
import pulsador
import presionATM
import voltimetro
import lluvia
import welcome

Este es el fichero principal, es el encargado de poner en funcionamiento la medidición de todos los sensores.
Lo primero y para no ser groseros saludamos por pantalla, llamamos a la clase "welcome.getbienvenida()".

# Pantalla de bienvenida
welcome.getbienvenida()
print("")

En nuestra Aplicacion usamos Threads o Hilos para controlar el ventilador del sistema y el botón que enciende la pantalla Led.

class runfan(threading.Thread):
    # Controla el ventilador de la CPU.
    def __init__(self, id):
        threading.Thread.__init__(self)
        self.id = id

    def run(self):

        while True:
            sem.acquire()
            funCpu.fun_evaluate()

            time.sleep(self.id + 30)
            sem.release()

class pulsOLED(threading.Thread):
    # Controla si pulsamos el boton que enciende la pantalla.
    def __init__(self, id):
        threading.Thread.__init__(self)
        self.id = id

    def run(self):

        sem.acquire()
        pulsador.Oled()
        sem.release()

f = runfan(0)
f.start()
#
p = pulsOLED(1)
p.start()

Para el resto de sensores usamos un bucle "While True" infinito para que vayan optienendo los datos de las diferentes clases. Los datos se suben de la Raspberry al Servidor cada 10 minutos, esto es modificable en el código Python. Como podemos ver al final del texto siguiente.

while True:

    print("Calculando UV")
    print("------------->>>")
    UV_ant = UV.calcula_UV(UV_ant)

    print("Calculando Luz")
    print("-------------->>>")
    Luz_ant = fotoresistor.main(Luz_ant)

    print("Calculando Humedad y Temperatura")
    print("-------------------------------->>>")
    Temp_ant, Hum_ant = sen_humedad.calcula_humedad(Temp_ant, Hum_ant)

    print("Calculando Presion Atmosferica")
    print("------------------------------>>>")
    Pressure_ant = presionATM.main(Pressure_ant)

    print("Mirando por la ventana por si llueve")
    print("------------------------------------>>>")
    LLuvia_ant = lluvia.Control_lluvia(LLuvia_ant)

    # voltimetro.calcula_bateria()

    #Subimos los ficheros al servidor cada 10 minutos
    i = i+1
    if i == 10:
        updatorSFTP.updator()
        i = 0
    time.sleep(60)

Sensor de Humedad/Temperatura, DHT22

Nombre del fichero: sen_humedad.py

Librería utilizadas:

#Modulos Sistema
import adafruit_dht
import datetime
import board

#Modulos Propios
import writefile

Cabe destacar como parte del código importante que estamos usando el pin D4, equivale al GPIO4 (conector 7), para conectar el sensor a la Raspberry.

dhtDevice = adafruit_dht.DHT22(board.D4)
temperature = dhtDevice.temperature
humidity = dhtDevice.humidity

Nos intersa saber en que momento hacemos la medida, siempre lo calcularemos en UTC, para evitar dudas sobre el uso horario.

#Generamos la hora
utc = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')

Finalmente sólo nos quedamos con el valor medido si hay una diferencia de ± 0,5º para la Temperatura y ± 1% para la Humedad.

if temperature>temp_ant + 0.5 or temperature < temp_ant - 0.5:
    temp_ant=temperature
    print ("\nTEMPERATURA")
    print ("-----------")
    writefile.setfile("temperatura",temp_ant)

    print (utc)
    print ("T ", temperature)
    print("")

    #Guardamos los valores solo cuando haya un cambio +-1%
if humidity>hum_ant + 1 or humidity < hum_ant - 1:
    hum_ant=humidity
    print ("\nHUMEDAD")
    print ("-------")
    writefile.setfile("humedad",hum_ant)

    print (utc)
    print ("H ", humidity)
    print("")

return(temperature,humidity)

Control del Ventilador Rasberry

Nombre del fichero: funCpu.py

Librería utilizadas:

#Modulos Sistema
from time import sleep
import RPi.GPIO as GPIO
from gpiozero import CPUTemperature
import writefile

Cabe destacar como parte del código importante que estamos usando el pin D22, equivale al GPIO25, para conectar el sensor a la Raspberry. En nuestro caso el ventilador se pone en funcinamiento cuando llega a los 45,1ºC.

try:
    pin = 25  # Gpio25=pin 22
    maxTMP = 45.1

Se envía al método writefile.setfile, es el encargado de guardar los datos en un fichero CSV. Esta operación se repite cada 40 Segundo.

 writefile.setfile("Fan", round(CPU_temp, 2))
    # Read the temperature every 40 sec, increase or decrease this limit if you want
    sleep(40)

Sensor de Lluvia

Nombre del fichero: lluvia.py

Librería utilizadas:

#Modulos Sistema
import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

#Modulos Propios
import writefile

Esta parte del código hace referencia al conversor MCP3008 que está conectado a la Raspberry por el pin D15, Gpio22. Esta parte es muy parecida a todos los sensores analógicos que necesita ser trasnformado su información de Analógico a Digital.

def Control_lluvia(LLuvia_ant):

    lluvia_ant = LLuvia_ant

    # create the spi bus
    spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)

    # create the cs (chip select)
    cs = digitalio.DigitalInOut(board.D22)

    # create the mcp object
    mcp = MCP.MCP3008(spi, cs)

Con el siguiente código evaluamos las lecturas que nos devuelve el sensor de lluvia. Evaluamos si la variable lluviasensor es igual o mayor a 1, si ocurre esto es que llueve si es menor a 1 no llueve.

try:

# En teoría debería ser 0 si llueve. Pero al usar un converso Analógico Digital no es cero nunca. Pero se acerca. De ahí que sea mayor de 1 cuando no llueve

lluviasensor = AnalogIn(mcp, MCP.P0).voltage

if (lluviasensor > 1):
    print("------")
    print("NO Llueve")
    print("------")

    lluvia = False

elif (lluviasensor >= 0.01):
    print("------")
    print("Llueve")
    print("------")

    lluvia = True

if (lluvia_ant != lluvia):

    print("Cambio de Tiempo")
    writefile.setfile("Lluvia", lluvia)
    lluvia_ant = lluvia

Welcome

Nombre del fichero: welcome.py

Librería utilizadas:

#Modulos Sistema
import datetime
import subprocess
import time
import board
import digitalio
import adafruit_ssd1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

#Modulos Propios
import obtenserial

A modo de saludo inicial cargamos una pequeña imagen de un ratón. Y a continuación durante unos segundos se puede obtenemos datos por pantalla, como la fecha, la IP y el número de serie de la Raspberry. Podemos jugar un poco para ubicar la información por pantalla usando las variables x y top. Obviamente el código no es mío, reutilicé uno que encontré por Internet.



    # Load image based on OLED display height.  Note that image is converted to 1 bit color.
    image = Image.open('Raton_Mini_h.png').resize((oled.width, oled.height), Image.ANTIALIAS).convert('1')
[...]

    cmd = "hostname -I"
    IP = subprocess.check_output(cmd, shell=True)

    IP = str(IP)
    IP = IP[2:IP.find(' ')]

    draw.text((x, top), str(datetime2.strftime("%d/%m/%Y %H:%M")) , font=font, fill=255)
    draw.text((x, top + 8), obtenserial.getserial(), font=font, fill=255)
    draw.text((x, top + 16), IP, font=font, fill=255)
[...]

Gestión del fichero XML, muestra.py y pantalla.xml

Nombre del fichero: muestra.py
Nombre del fichero: pantalla.xml

Librería utilizadas en muestra.py

#Modulos Sistema
import xml.etree.ElementTree as ET
import os

Usamos un fichero xml llamado "pantalla.xml" para ir almacenando el último valor que va obteniendo las raspberry antes de enviar los datos a Internet. Para leer el fichero xml y mostrar la información cuando se pulsa el botón hemos creado la clase def leeXML(), se encarga de navegar por el árbol XML por cada categoría y recoger los datos y guardarlos en un array de datos.

def leeXML():

tree = ET.parse(os.getcwd()+'/pantalla.xml')
root = tree.getroot()

contenedor = []

# Lista todos los elementos
for country in root.findall('Tipo'):
    valor = country.find('Valor').text
    titulo = country.find('Acro').text
    formato = country.find('Formato').text
    contenedor.append(titulo+" "+valor + formato)

return contenedor

Con df escribeXML(...) guardamos los datos actualizados en el XML, ojo solo guardamos el último valor por cada categoria. Los anteriores desaparecen para siempre. Recordamos que solo usamos el fichero XML para mostrar la información por la pantalla LED. Los datos son enviados periódicamente a la BBDD ubicada fuera de la Raspberry.

def escribeXML(campo, valor, fecha):

tree = ET.parse(os.getcwd()+'/pantalla.xml')
root = tree.getroot()

for country in root.findall("./*[@Titulo='"+campo+"']"):

    for mi in country.iter("Valor"):
        mi.text = str(round(valor, 2))

    for mi in country.iter("Fecha"):
        mi.text = str(fecha)

tree.write(os.getcwd()+'/pantalla.xml')

Tenemos organizados el árbol XML en 7 categorías, una por cada sensor. El campo Acro viene a ser la abreviatura, Valor el último valor obtenido, Formato es la unidad de medida utilizada, Fecha cuando se obtuvo la medida y el campo Enable no lo uso, pero sería para ocultar la aparición en la pantalla.

<Sensores>
	<Tipo Titulo="humedad">
		<Acro>H</Acro>
		<Valor>76.3</Valor>
		<Formato>%</Formato>
		<Fecha>2021-09-26 19:27:12</Fecha>
		<Enable>True</Enable>
	</Tipo>
	<Tipo Titulo="temperatura">
		<Acro>T</Acro>
		<Valor>25.1</Valor>
		<Formato>ºC</Formato>
		<Fecha>2021-09-26 19:27:12</Fecha>
		<Enable>True</Enable>
	</Tipo>
	<Tipo Titulo="UV">
		<Acro>UV</Acro>
		<Valor>0.18</Valor>
		<Formato>mW...</Formato>
		<Fecha>2021-09-26 19:27:02</Fecha>
		<Enable>True</Enable>
	</Tipo>
	<Tipo Titulo="luz">
		<Acro>L</Acro>
		<Valor>73.33</Valor>
		<Formato>lm</Formato>
		<Fecha>2021-09-26 19:27:07</Fecha>
		<Enable>True</Enable>
	</Tipo>
	<Tipo Titulo="Fan">
		<Acro>CPU</Acro>
		<Valor>45.46</Valor>
		<Formato>ºC</Formato>
		<Fecha>2021-09-26 19:27:32</Fecha>
		<Enable>True</Enable>
	</Tipo>
	<Tipo Titulo="PAtmosferica">
		<Acro>P.</Acro>
		<Valor>1014.84</Valor>
		<Formato>hPa</Formato>
		<Fecha>2021-09-26 19:27:12</Fecha>
		<Enable>True</Enable>
	</Tipo>
		<Tipo Titulo="Lluvia">
		<Acro>LL.</Acro>
		<Valor>0</Valor>
		<Formato>hPa</Formato>
		<Fecha>2021-09-26 19:18:44</Fecha>
		<Enable>True</Enable>
	</Tipo>
</Sensores>

ObtenSerial

Nombre del fichero: obtenserial.py

Obtiene el número de serie de la RaspBerry, todos los registros son guardados en al base de datos con el número de serie, esto nos permitirá en un futuro ampliar la red con otras Raspberrys en otras ubicaciones.

def getserial():

# Extract serial from cpuinfo file

cpuserial = "0000000000000000"

try:

    f = open('/proc/cpuinfo','r')

    for line in f:

        if line[0:6]=='Serial':
            cpuserial = line[10:26]

    f.close()

except:
    cpuserial = "ERROR000000000"
return cpuserial

FotoResistor

Nombre del fichero: fotoresistor.py

Librería utilizadas:

#Modulos Sistema
import smbus
import time
import writefile
import datetime

En este caso la dirección de I2C del hardawre es 0x23. Podemos obenter la información ejecutando el comando sudo i2cdetect -y 1, al final de este apartado explico como obtenerlo y que elemento es cada dirección.

# Define some constants from the datasheet

DEVICE = 0x23  # Default device I2C address

Realizamos una evaluación de las medidas cada 5 segundos y guardamos los valores si hay una variación de ± 5%.

def main(Luz_ant):

suma_luz = 0
suma_ant = Luz_ant

try:

    lightLevel = readLight()
    suma_luz = float(lightLevel)

    if suma_luz > 0:

        # Enviamos a la web cuando hay cambios del +-5%

        if suma_luz > suma_ant+(suma_ant*5/100) or suma_luz < suma_ant - (suma_ant*5/100):

            # Generamos la hora
            utc = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')

            suma_ant = suma_luz
            print("LUZ")
            print("---")
            print(utc)
            print("L " + str(suma_luz))
            print("")

            # Redondeamos a 2 decimales para que la BBDD guarde bien los datos
            writefile.setfile("luz", round(suma_luz, 2))

    else:
        print("error Revisa el Sistema de Fotoresistor")

    time.sleep(5)
except (KeyboardInterrupt, SystemExit):
    raise
return(suma_ant)

En nuestro caso tenemos las siguiente direciones I2C:

aste@Serv-Jardin:~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- 23 -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 --

En nuestro caso las direcciones correponden a:

  • 23 - FotoreSistor - BH1750.
  • 3c - Pantalla Oled - SH1106.
  • 76 - Presión Atmosférica- BME280.
  • Pulsador

    Nombre del fichero: pulsador.py

    Librería utilizadas:

    #Modulos Sistema
            import datetime
    import subprocess
    import time
    import adafruit_ssd1306
    from PIL import Image
    from PIL import ImageDraw
    from PIL import ImageFont
    import busio
    import digitalio
    import board
    import adafruit_mcp3xxx.mcp3008 as MCP
    from adafruit_mcp3xxx.analog_in import AnalogIn
    
    #Modulos Propios
    import muestra
    

    La función del pulsador es mostrar por la pantalla Led una serie de información como puede ser la fecha, la temperatura la humedad... para ello lee la información de un fichero XML, donde vamos guardando los últimos valores obtenidos.

    En teoría cuando pulsas el botón empieza a circular la electricidad. Hasta el momento no hemos encontrado el motivo que aunque no se pulse el botón hay algo de circulación eléctrica.

    try:
        while (True):
            # Como existe ruido, hemos tenido de dejar de usar .value!=0): Asi ignoramos el ruido
            if (AnalogIn(mcp, MCP.P4).value > 2000):
    

    Esta parte del código va destinada para crear la salida de pantalla.
    Con muestra.leeXML(), llamamos a la clase encargada de leer el fichero XML con la información.

    # Clear display.
    oled.fill(0)
    oled.show()
    
    # Create blank image for drawing.
    # Make sure to create image with mode '1' for 1-bit color.
    width = oled.width
    height = oled.height
    image = Image.new('1', (width, height))
    # Get drawing object to draw on image.
    draw = ImageDraw.Draw(image)
    # Draw a black filled box to clear the image.
    draw.rectangle((0, 0, width, height), outline=0, fill=0)
    # Draw some shapes.
    # First define some constants to allow easy resizing of shapes.
    padding = -2
    top = padding
    bottom = height - padding
    # Move left to right keeping track of the current x position for drawing shapes.
    x = 0
    # Load default font.
    #font = ImageFont.load_default()
    # Alternatively load a TTF font.  Make sure the .ttf font file is in the same directory as the python script!
    # Some other nice fonts to try: http://www.dafont.com/bitmap.php
    font = ImageFont.truetype('Minecraftia-Regular.ttf', 8)
    datetime2 = datetime.datetime.now()
    # Draw a black filled box to clear the image.
    draw.rectangle((0, 0, width, height), outline=0, fill=0)
    # Shell scripts for system monitoring from here : https://unix.stackexchange.com/questions/119126/ommand-to-display-memory-usage-disk-usage-and-cpu-load
    cmd = "hostname -I"
    IP = subprocess.check_output(cmd, shell=True)
    IP = str(IP)
    IP = IP[2:IP.find(' ')]
    draw.text((x, top), str(datetime2.strftime(
        "%d/%m/%Y %H:%M")), font=font, fill=255)
    draw.text((x, top + 9), IP, font=font, fill=255)
    draw.text((110, top), ("BAT"), font=font, fill=255)
    draw.text((105, top+9), ("100%"), font=font, fill=255)
    info = muestra.leeXML()
    ix = 2
    i = 0
    for salida in info:
        if i == 0:
            draw.text((x, top + 9*ix), salida, font=font, fill=255)
            i = 1
        elif i == 1:
            draw.text((x+45, top + 9*ix), "| " +
                        salida, font=font, fill=255)
            i = 2
        else:
            draw.text((x+90, top + 9*ix), "| " +
                        salida, font=font, fill=255)
            i = 0
            ix = ix+1
    # Display image.
    oled.image(image)
    oled.show()
    time.sleep(10)
    # Clear display.
    oled.fill(0)
    oled.show()

    UV

    Nombre del fichero: UV.py

    Librería utilizadas:

    #Modulos Sistema
    import datetime
    import time
    import busio
    import digitalio
    import board
    import adafruit_mcp3xxx.mcp3008 as MCP
    
    #Modulos Propios
    import convert_ard_TO_ras
    import writefile
    

    Cabe destacar como parte del código importante que estamos usando el pin 22, equivale al GPIO22 (conector 15), para conectar el sensor a la Raspberry.

    def calcula_UV(UV_ant):
    
        i = 0
        suma_UV = 0
        suma_ant = UV_ant
        suma_3v3 = 0
    
        # create the spi bus
        spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
    
        # create the cs (chip select) Orignalmente D5 (Gpio5, conector 29)
        cs = digitalio.DigitalInOut(board.D22)
    
        # create the mcp object
        mcp = MCP.MCP3008(spi, cs)
        try:
            while i < 12:
            # Canal 1
                valor_UV = AnalogIn(mcp, MCP.P1).value
    
            # Canal 2
                valor_3v3 = AnalogIn(mcp, MCP.P2).value
    

    Recuperamos los valores del sensore de UV, y como la fórmula usada está pensada para Arduino transformamos los valores, un poco más adelante explicamos con que clase lo realizamos. No se muestra ni se explica el algoritmo aplicado lo encontrarás en el fichero Python correspondienete.

    # Homogeneizamos los valores del MPC3008 al ADC de arduino
    valor_UV = float(convert_ard_TO_ras.Con_ras3v3_TO_ard(valor_UV))
    valor_3v3 = float(convert_ard_TO_ras.Con_ras3v3_TO_ard(valor_3v3))
    [...]
    

    Se evalua los datos cada 1 minutos, con 12 tomas realizadas cada 5 segundos y subiermos los datos a la web cuando haya un cambio de +- 0,5. Guardaremos los datos con hora UTC, de esta manera evitamos discrepancias horarias de la toma de datos.

    [...]
    # Generamos la hora
    utc = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
    
    # Enviamos a la web cuando hay cambios del +-0,5 del valor
    
    # Ponemos 0 porque el valor por defecto es 0 y necesitamos una primera toma para que pueda evaluar.
    if suma_ant==0 or suma_UV >= suma_ant+(0.5) or suma_UV <= suma_ant - (0.5):
    
        suma_ant = suma_UV
    
        print("UV")
        print("------------------")
        print(utc)
        print("UV " + str(suma_UV))
        print("------------------")
    
        # Redondeamos a 2 decimales para que la BBDD guarde bien los datos
        writefile.setfile("UV", round(suma_UV, 2))
    
    time.sleep(5)
    

    CONVERSOR ARDUINO A RASPBERRY

    Nombre del fichero: convert_ard_TO_ras.py

    En este fichero tiene unas clases para convertir los valores dados por el conversor Analógico-Digital MPC3008 a valores similares que daría un Arduino y viceversa. Las clases hace posible que usemos scripts para Arduino y una vez que los hayamos transformado usar la salida Analógica que nos de el MPC3008 con las fórmulas propuestas inicialmente por el Script del Arduino.

    Podemos transformar los valores de la siguiente manera:

  • Con_ras_TO_ard Convierte los valores obtenidos del MPC3008 de las Raspberry a un Arduino con sensores de 5v.
  • Con_ras3v3_TO_ard Convierte los valores obtenidos del MPC3008 de las Raspberry a un Arduino con sensores de 3.3v.
  • Con_ard_TO_Ras Convierte los valores obtenidos de un Arduino con sensores de 5v a un valor del MPC3008 de las Raspberry.
  • Con_ard_TO_ras3v3 Convierte los valores obtenidos de un Arduino con sensores de 3.3v a un valor del MPC3008 de las Raspberry.
  • def Con_ras_TO_ard(valor):
    
        #Homologamos un valor obtenido de MPC3008 a un valor de Arduino de 5v
        #Usar este si trabajas con MPC3008 y 5v y el codigo de ejemplo es de arduino
    
        resultado= (1023*valor)/65535
        return (resultado)
    
    def Con_ard_TO_Ras(valor):
    
        #Homologamos un valor obtenido en un Arduino de 5v a un valor MPC3008
    
        resultado=(65535*valor)/1023
        return (resultado)
    
    def Con_ras3v3_TO_ard(valor):
    
        #Homologamos un valor obtenido de MPC3008 a un valor de Arduino 3.3v
        #Usar este si trabajas con MPC3008 y 3.3v y el codigo de ejemplo es de arduino
    
        valor=Con_ras_TO_ard(valor)
        resultado= (valor*3.3)/5
        return (resultado)
    
    def Con_ard_TO_ras3v3(valor):
    
        #Homologamos un valor obtenido en un Arduino 3.3v a un valor MPC3008
    
        valor=Con_ard_TO_Ras(valor)
        resultado=(5*valor)/3.3
        return (resultado)
    

    Update To SFTP

    Nombre del fichero: updatorSFTP.py

    Librería utilizadas:

    #Modulos Sistema
    import paramiko
    import os
    import datetime
    
    #Modulos Propios
    import writefile
    

    La función de este fichero es preprar los ficheros para subirlos a la plataforma web.

    def updator():
    
        try:
    
            # Cargamos las variables de uso
            variablesuso()
    
            global pathBase
    
            # Me aseguro de guardar una variable del path principal
            pathBase = os.getcwd()
    
            # --Escaneamos ficheros en /csv
            file_scan = Scanfile()
    
            # Cambiamos el directorio de trabajo
    
            if (os.getcwd().find('/csv') == -1):
    
                old_parent = os.getcwd()
                os.chdir(os.getcwd() + "/csv")
    
            # --Leemos el array y subimos al FTP
            for item in file_scan:
    
                log = (upload(item))
    
                writefile.setlog("ftp", log, pathBase)
                print("Fichero enviado a Web")
                print("-------------------------------")
                print(log)
    
            # Nos aseguramos que el directorio de trabajo sea desde el que estamos ejecutando la aplicacion
            os.chdir(old_parent)
        except (KeyboardInterrupt, SystemExit):
            raise
    

    En esta parte del fichero se encarga de buscar en un directorio dado todos los ficheros con extensión CSV.

    def Scanfile():
    
        fichero_CSV = []
    
        for file in os.listdir("./csv"):
            if file.endswith(".csv"):
    
                fichero_CSV.append(file)
    
        return (fichero_CSV)
    

    Con esta parte del código gestionamos e imprimimos por pantalla los ficheros CSV que se van enviando a la plataforma Web.

    def upload(file):
    
        # Try me aseguro que si no hay conexion continue ejecutandose el FTP aunque el servier este caido
        utc = datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S-')
        utc_log = datetime.datetime.utcnow().strftime('%Y/%m/%d %H:%M:%S -')
    
        print("-*-*-*-*-*-*-*-*-*-*-*-*")
        print(ftp_server)
        print("-*-*-*-*-*-*-*-*-*-*-*-*")
    
        try:
    
            ssh_transport = paramiko.Transport(ftp_server, 22)
            # Sin clave ssl hace falta password
            ssh_transport.connect(username=ftp_user, password=ftp_pass)
            # Con clave ssl no hace falta password
            #pk = paramiko.RSAKey.from_private_key_file('/home/aste/.ssh/id_rsa')
            #ssh_transport.connect(username=ftp_user, pkey=pk)
    
            sftp_session = paramiko.SFTPClient.from_transport(ssh_transport)
    
            # Renombramos el fichero
            file_new = utc + file
            os.rename(file, file_new)
    
            ext = os.path.splitext(file_new)[1]
    
            if ext in (".txt", ".htm", ".html", ".csv"):
    
                # Sube los ficheros por SFTP
                sftp_session.put(file_new, PathCSV+"/"+file_new)
    
                salida = (utc_log + " Fichero %s UP OK" % file_new)
    
                # Borramos el fichero subido por SFTP
                os.remove(file_new)
                erasefile = (utc_log + " Fichero %s DELETE" % file_new)
                writefile.setlog("delete", erasefile, pathBase)
    
            else:
    
                salida = (utc_log + " Fichero %s no esta permitido UP KO" % file_new)
    
            sftp_session.close()
            ssh_transport.close()
            # Except permite manejare el error
        except:
    
            # Si se pierde la conectividad mostrara este mensaje
            salida = (utc_log + " Servidor " + ftp_server + " INACCESIBLE")
            variablesuso()
    
            # Finally permite ejecutar codigo,independientemente del resultado de los bloques try y except.
        finally:
    
            print("-------------------------------")
            # Referescamos variables por si hay cambio de servidor
    
        return salida
    

    Definimos variables globales. La gracia de esta parte del fichero es que si necesitamos cambiar la dirección IP/URL del servidor FTP o sus credenciales, no es necesario parar la aplicación. Tan sólo con modificar los datos requeridos en el fichero variables3.dat sería suficiente para que empezara a subir.

    def variablesuso():
    
        # definos las varialbes Globales. Se hace asi por si se cambia de servidor no hay que parar la ejecucion de la APP solo modificar el fichero variable.dat
        global ftp_server, ftp_user, ftp_pass, FileCSV, PathCSV, host
    
        # Buscamos si el direcotrio de trabajo acaba en la carpeta CSV
        buscando_CSV = os.getcwd().find('/csv')
    
        # volvemos al espacio de trabajo inicial sin la carpeta csv
    
        if (buscando_CSV != -1):
    
            os.chdir(os.getcwd()[:buscando_CSV])
    
        fileName = "variables3.dat"
    
        fileObj = open(fileName)
    
        # Declaramos el array del servidor
        params = {}
    
        for line in fileObj:
    
            line = line.strip()
            if not line.startswith("#"):
    
                key_value = line.split("=")
    
                if len(key_value) == 2:
                    params[key_value[0].strip()] = key_value[1].strip()
    
        ftp_server = params["ftp_server"]
        ftp_user = params["ftp_user"]
        ftp_pass = params["ftp_pass"]
        FileCSV = params["ftp_server"]
        PathCSV = params["Serv_PathCSV"]
        host = params["host"]
    
        fileObj.close()
    

    Valores Centalizado

    Nombre del fichero: variables3.dat

    En este fichero almacenaremos la IP o URL del servidor FTP (ftp_server), el Usuario (ftp_user) y la Contraseña (ftp_pass). Como también la ruta de trabajo (Serv_PathCSV). FileCSV y host no se están implementando para ningún desarrollo.

    Un dato importante a saber, NO es necesario parar la aplicación de Python para poder cambiar algún parámetro del Servidor FTP, modificándolo en el fichero variables3.dat ya se efectua el cambio en caliente.

    #David Marchena
    #Valores centralizados
    
    ftp_server = 192.168.1.97
    ftp_user = miusuario
    ftp_pass = micontraseña
    Serv_PathCSV = /var/www/catalana.ga/csv
    FileCSV = "./csv"
    host = http://192.168.1.97/updator.php
    

    Presión Atmosférica

    Nombre del fichero: presionATM.py

    Librería utilizadas:

    #Modulos Sistema
    import smbus
    import datetime
    import time
    from ctypes import c_short
    from ctypes import c_byte
    from ctypes import c_ubyte
    
    #Modulos Propios
    import writefile
    

    Indicar que en el dispositivo utilizado puede medir Humedad y Temperatura, pudiendo ser alternativa al DHT22. En nuestro caso no lo usamos porque no devuelve datos de la humedad. Sólo guardamos datos si varía la presión Atmosférica. Si la presión Atmosférica no varia no subimos datos a la base de datos. De esta manera optimizamos recursos. Como siempre usamos la sentencia writefile.setfile(...) para gestionar la subida de datos. Si por el motivo que fuera no devolviera un dato válido en el prompt de la Raspberry aparecía un mensaje de error.

    def main(Pressure_ant):
    
    pressures_ant = Pressure_ant
    temperature, pressure, humidity = readBME280All()
    
    try:
    
        if pressure != 0:
    
            utc = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
    
            if pressure > (pressures_ant+1) or pressure < (pressures_ant - 1):
    
                pressures_ant = pressure
    
                print("Presion Atomsoferica")
                print("--------------------")
                print(utc)
                print("hPa " + str(pressure))
                print("")
    
                writefile.setfile("PAtmosferica", round(pressure, 2))
    
            pressure = 0
    
        else:
            print("error Revisa el Sistema de Presion Atmosferica")
    
    except (KeyboardInterrupt, SystemExit):
        raise
    return pressures_ant
    

    Voltímetro

    Nombre del fichero: voltimetro.py

    Librería utilizadas:

    #Modulos Sistema
    import datetime
    import time
    import busio
    import digitalio
    import board
    import adafruit_mcp3xxx.mcp3008 as MCP
    from adafruit_mcp3xxx.analog_in import AnalogIn
    
    # #Modulos Propios
    import writefile
    import convert_ard_TO_ras
    
    

    Esta parte de la aplicación está pensado para integrarlo con placas solares y poder controlar a distancia la carga de la baterías. Esta parte aún está en desarrollo. Tan pronto como tenga avances los actualizaré.

    def calcula_bateria():
    
        i = 0
        suma_bateria = 0
        suma_ant = 0
    
        # create the spi bus
        spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
    
        # create the cs (chip select) Orignalmente D5 (Gpio5, conector 29)
        cs = digitalio.DigitalInOut(board.D22)
    
        # create the mcp object
        mcp = MCP.MCP3008(spi, cs)
    
        try:
            while True:
    
                Vin=AnalogIn(mcp, MCP.P5).voltage
    
                #usamos una bateria de 12v y el ADC solo le voltajes de hasta 3.3v
                bateria=(12*Vin)/3.3
    
                # Evaluamos la bateria cada 12 periodos de 5 segundos por tanto 1 minuto
                suma_bateria = suma_bateria + bateria
                i = i + 1
    
                if suma_bateria!=0:
    
                    if i == 12:
                        #Hacemos una media en un minuto del voltaje en la bateria
                        suma_bateria = suma_bateria / 12
    
                        # Generamos la hora
                        utc = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
                        i = 0
    
                        # Enviamos a la web cuando hay cambios del +-1%
    
                        if suma_bateria > suma_ant+(suma_ant*1/100) or suma_bateria< suma_ant -(suma_ant*1/100):
    
                            suma_ant=0
                            suma_ant = suma_bateria
                            print ("Bateria")
                            print ("-------")
                            print (utc)
                            print ("V "+ str(suma_bateria))
                            print ("")
    
                            #Redondeamos a 2 decimales para que la BBDD guarde bien los datos
                            writefile.setfile("Bateria", round (suma_bateria,2))
    
                            #ponemos el contador a 0
                            suma_bateria = 0
                else:
                    print("error Revisa el Sistema de Baterias")
    
                time.sleep(5)
    
        except (KeyboardInterrupt, SystemExit):
            raise
    

    Generador de Ficheros, CSV, LOG y XML

    Nombre del fichero: writefile.py

    Librería utilizadas:

    #Modulos Sistema
    import datetime
    import os
    
    #Modulos Propios
    import obtenserial
    import muestra
    

    Esta es la parte de la aplicación que se encarga de genrar de crear los ficheros CSV y manteneder actualizado el fichero XML. Como siempre usaremos para guardar los datos la hora UTC.

    def setfile(file, value):
    
        # Obtiene elnumero de serie de la raspberry
        piserial = obtenserial.getserial()
    
        utc = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
    
        buscando_CSV = os.getcwd().find('/csv')
    
        # volvemos al espacio de trabajo inicial sin la carpeta csv
    
        if (buscando_CSV != -1):
    
            caminosanto = os.getcwd() + "/"
        else:
            caminosanto = os.getcwd() + "/csv/"
    
        f = open(caminosanto + file + ".csv", "a")
        f.write(utc + "," + str(value) + "," + piserial + "\n")
        f.close()
    
        #actualizamos el fichero XML
        muestra.escribeXML(file,round (value,2),utc)
    
    def setlog(file, value, ruta_primaria):
    
        f = open(ruta_primaria + "/log/" + file + ".log", "a")
        f.write(str(value) + "\n")
        f.close()
    

    BIBILIOGRAFÍA