5 – Lectura de sensores con Python y almacenamiento automático en MariaDB

En esta práctica vas a convertir tu Raspberry Pi en un pequeño nodo autónomo de captura de datos dentro del proyecto RaspyAlarma.

Tu Raspberry deberá ser capaz de:

  1. leer los datos de un sensor con Python,
  2. guardar esas lecturas en una base de datos MariaDB,
  3. ejecutar ese proceso automáticamente cada cierto tiempo mediante cron,
  4. y dejar preparada la información para una futura sincronización con un servidor central.

Todos los alumnos deberán usar la misma estructura de base de datos, ya que más adelante será necesario unificar los datos de todos los sensores y de todas las Raspberry del proyecto.


Objetivo general

El objetivo de esta práctica es crear un sistema automático en el que una Raspberry:

  • lea un sensor,
  • almacene la lectura en una base de datos local,
  • identifique de qué Raspberry procede el dato,
  • indique si la lectura representa o no una situación de alerta,
  • y marque si ese dato ya fue enviado o no al servidor central.

Estructura obligatoria de la base de datos

Todos los alumnos deberán usar una base de datos llamada exactamente:

raspialarm

Y una tabla llamada exactamente:

lecturas_sensores

Estructura obligatoria de la tabla

La tabla deberá contener estos campos:

  • id
  • raspberry_id
  • nombresensor
  • lectura1
  • lectura2
  • lectura3
  • fecha_hora
  • alumnoEncargado
  • descripcionSensor
  • estado_alerta
  • enviado_central

Significado de los campos

raspberry_id

Este campo servirá para identificar qué Raspberry ha generado la lectura.

Ejemplos válidos:

  • RPI01
  • RPI_AULA_01
  • RPI_PASILLO
  • RPI_HABITACION_3

Este identificador debe ser fijo para cada Raspberry y no debe cambiar entre ejecuciones.


estado_alerta

Este campo servirá para indicar el estado de la lectura.

Valores recomendados:

  • normal
  • aviso
  • critico

No todos los sensores tendrán alertas complejas al principio, pero el campo debe existir ya desde esta fase.

Ejemplo:

  • una lectura dentro de lo normal → normal
  • una lectura elevada pero no crítica → aviso
  • una lectura peligrosa o fuera de umbral → critico

enviado_central

Este campo servirá para saber si esa lectura ya ha sido enviada al servidor central del proyecto.

Valores recomendados:

  • 0 → todavía no se ha enviado
  • 1 → ya se ha enviado

En esta práctica, normalmente todos los registros nuevos se guardarán inicialmente con valor 0.


Parte 1. Crear la base de datos

Paso 1. Comprobar que MariaDB está activa

Abre una terminal y ejecuta:

sudo systemctl status mariadb

Si no estuviera arrancada:

sudo systemctl start mariadb

Y para dejarla activada al iniciar el sistema:

sudo systemctl enable mariadb

Paso 2. Entrar en MariaDB

sudo mariadb

Paso 3. Crear la base de datos

Dentro de MariaDB:

CREATE DATABASE raspialarm;
SHOW DATABASES;
USE raspialarm;

Parte 2. Crear la tabla común del proyecto

Ejecuta esta instrucción SQL exactamente como aparece:

CREATE TABLE lecturas_sensores (
id INT AUTO_INCREMENT PRIMARY KEY,
raspberry_id VARCHAR(50) NOT NULL,
nombresensor VARCHAR(100) NOT NULL,
lectura1 DECIMAL(10,2),
lectura2 DECIMAL(10,2),
lectura3 DECIMAL(10,2),
fecha_hora DATETIME NOT NULL,
alumnoEncargado VARCHAR(100) NOT NULL,
descripcionSensor VARCHAR(255),
estado_alerta VARCHAR(20) NOT NULL DEFAULT 'normal',
enviado_central TINYINT(1) NOT NULL DEFAULT 0
);

Paso 4. Comprobar la estructura de la tabla

DESCRIBE lecturas_sensores;

También puedes verla completa con:

SHOW CREATE TABLE lecturas_sensores;

Parte 3. Crear un usuario específico para la aplicación

No debes usar root dentro del script Python.

Ejecuta:

CREATE USER 'raspiuser'@'localhost' IDENTIFIED BY 'raspi1234';
GRANT ALL PRIVILEGES ON raspialarm.* TO 'raspiuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Parte 4. Crear la estructura de carpetas

Ejecuta:

mkdir -p /home/pi/raspialarma/sensores
mkdir -p /home/pi/raspialarma/logs
mkdir -p /home/pi/raspialarma/docs

Comprueba el resultado:

ls -R /home/pi/raspialarma

Parte 5. Instalar el conector de Python para MariaDB

Ejecuta:

sudo apt update
sudo apt install python3-pip python3-mysqldb -y

Parte 6. Crear primero un script de prueba con datos fijos

Antes de leer el sensor real, primero debes comprobar que:

  • Python puede conectarse a MariaDB,
  • el INSERT funciona,
  • y las nuevas columnas también se guardan correctamente.

Edita el archivo:

nano /home/pi/raspialarma/sensores/captura_sensor.py

Pega este contenido:

#!/usr/bin/env python3

import MySQLdb
from datetime import datetime

def escribir_log(mensaje):
    with open("/home/pi/raspialarma/logs/raspialarma.log", "a") as log:
        log.write(f"{datetime.now()} - {mensaje}\n")

try:
    conexion = MySQLdb.connect(
        host="localhost",
        user="raspiuser",
        passwd="raspi1234",
        db="raspialarm"
    )

    cursor = conexion.cursor()

    raspberry_id = "RPI01"
    nombresensor = "SENSOR_PRUEBA"
    lectura1 = 25.50
    lectura2 = 60.00
    lectura3 = 0.00
    fecha_hora = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    alumnoEncargado = "TU_NOMBRE"
    descripcionSensor = "Prueba inicial de inserción en MariaDB"
    estado_alerta = "normal"
    enviado_central = 0

    sql = """
        INSERT INTO lecturas_sensores
        (raspberry_id, nombresensor, lectura1, lectura2, lectura3, fecha_hora,
         alumnoEncargado, descripcionSensor, estado_alerta, enviado_central)
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
    """

    valores = (
        raspberry_id,
        nombresensor,
        lectura1,
        lectura2,
        lectura3,
        fecha_hora,
        alumnoEncargado,
        descripcionSensor,
        estado_alerta,
        enviado_central
    )

    cursor.execute(sql, valores)
    conexion.commit()

    mensaje = "Lectura guardada correctamente."
    print(mensaje)
    escribir_log(mensaje)

except Exception as e:
    error = f"Error: {e}"
    print(error)
    escribir_log(error)

finally:
    try:
        cursor.close()
        conexion.close()
    except:
        pass

El INSERT debe hacerse desde Python

En esta práctica no se insertarán datos desde PHP ni desde formularios web.
La inserción de datos se realizará directamente desde un script Python.

El script debe ejecutarse automáticamente

El script Python deberá ejecutarse cada cierto tiempo utilizando cron.

Debes documentar lo que significa cada lectura

Cada sensor es diferente. Por tanto, deberás dejar claro en tu documentación qué representa:

  • lectura1
  • lectura2
  • lectura3

Por ejemplo, en un DHT11 podrían significar:

  • lectura1 = temperatura
  • lectura2 = humedad
  • lectura3 = no usada

Posible estructura de significado de las lecturas

Para que todos trabajen de forma homogénea, puedes pedirles una tabla como esta en su documentación:

Tipo de sensorlectura1lectura2lectura3
DHT11 / DHT22TemperaturaHumedadNo usada
HC-SR04DistanciaNo usadaNo usada
PIRMovimiento detectado (0/1)No usadaNo usada
MQ-2Nivel de gas/humoUmbralNo usada
LDRIntensidad de luzNo usadaNo usada

Paso 2. Personalizar el script

Debes modificar al menos estos valores:

raspberry_id = "RPI01"
alumnoEncargado = "TU_NOMBRE"

Sustitúyelos por valores reales.

Ejemplo:

raspberry_id = "RPI_AULA_03"
alumnoEncargado = "Antonio"

Paso 3. Dar permisos de ejecución

chmod +x /home/pi/raspialarma/sensores/captura_sensor.py

Paso 4. Ejecutar el script manualmente

python3 /home/pi/raspialarma/sensores/captura_sensor.py

Si todo ha ido bien, verás:

Lectura guardada correctamente.

Parte 7. Comprobar que el INSERT ha funcionado

Entra en MariaDB:

mariadb -u raspiuser -p

Contraseña:

raspi1234

Dentro de MariaDB:

USE raspialarm;
SELECT * FROM lecturas_sensores;

Comprueba que aparecen correctamente:

  • raspberry_id
  • estado_alerta
  • enviado_central

Parte 8. Sustituir los datos fijos por la lectura real del sensor

Cuando el script de prueba funcione, sustituye los valores fijos por los datos reales del sensor.

La tabla seguirá siendo la misma.
Solo cambiarán los valores que insertas.


Parte 9. Cómo decidir el estado de alerta

Debes programar una lógica sencilla para determinar el valor de estado_alerta.

Ejemplo general

if lectura1 < 30:
estado_alerta = "normal"
elif lectura1 < 50:
estado_alerta = "aviso"
else:
estado_alerta = "critico"

Esto es solo un ejemplo. Cada sensor deberá definir sus propios umbrales.


Ejemplos según sensor

Sensor de temperatura

  • temperatura normal → normal
  • temperatura alta → aviso
  • temperatura muy alta → critico

Sensor de gas

  • valor bajo → normal
  • valor elevado → aviso
  • valor peligroso → critico

Sensor PIR

  • sin movimiento → normal
  • movimiento detectado → puede mantenerse en aviso o critico, según el criterio del proyecto

Sensor de distancia

  • distancia segura → normal
  • objeto cercano → aviso
  • objeto muy cercano → critico

Parte 10. Valor inicial de enviado_central

Todos los registros nuevos deberán guardarse inicialmente así:

enviado_central = 0

Esto significa que todavía no han sido enviados al servidor central.

Más adelante, cuando construyas el sistema de sincronización, los registros enviados se actualizarán a:

enviado_central = 1

Parte 11. Ejemplo de script con lógica de alerta

#!/usr/bin/env python3

import MySQLdb
from datetime import datetime

def escribir_log(mensaje):
    with open("/home/pi/raspialarma/logs/raspialarma.log", "a") as log:
        log.write(f"{datetime.now()} - {mensaje}\n")

try:
    conexion = MySQLdb.connect(
        host="localhost",
        user="raspiuser",
        passwd="raspi1234",
        db="raspialarm"
    )

    cursor = conexion.cursor()

    raspberry_id = "RPI01"
    nombresensor = "SENSOR_PRUEBA"
    lectura1 = 42.00
    lectura2 = 0.00
    lectura3 = 0.00
    fecha_hora = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    alumnoEncargado = "TU_NOMBRE"
    descripcionSensor = "Prueba con niveles de alerta"
    enviado_central = 0

    if lectura1 < 30:
        estado_alerta = "normal"
    elif lectura1 < 50:
        estado_alerta = "aviso"
    else:
        estado_alerta = "critico"

    sql = """
        INSERT INTO lecturas_sensores
        (raspberry_id, nombresensor, lectura1, lectura2, lectura3, fecha_hora,
         alumnoEncargado, descripcionSensor, estado_alerta, enviado_central)
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
    """

    valores = (
        raspberry_id,
        nombresensor,
        lectura1,
        lectura2,
        lectura3,
        fecha_hora,
        alumnoEncargado,
        descripcionSensor,
        estado_alerta,
        enviado_central
    )

    cursor.execute(sql, valores)
    conexion.commit()

    mensaje = f"Lectura guardada correctamente con estado {estado_alerta}"
    print(mensaje)
    escribir_log(mensaje)

except Exception as e:
    error = f"Error: {e}"
    print(error)
    escribir_log(error)

finally:
    try:
        cursor.close()
        conexion.close()
    except:
        pass

Parte 12. Automatizar la ejecución con cron

Edita el crontab:

crontab -e

Añade una de estas opciones.

Cada minuto

* * * * * /usr/bin/python3 /home/pi/raspialarma/sensores/captura_sensor.py >> /home/pi/raspialarma/logs/cron.log 2>&1

Cada 5 minutos

*/5 * * * * /usr/bin/python3 /home/pi/raspialarma/sensores/captura_sensor.py >> /home/pi/raspialarma/logs/cron.log 2>&1

Cada 10 minutos

*/10 * * * * /usr/bin/python3 /home/pi/raspialarma/sensores/captura_sensor.py >> /home/pi/raspialarma/logs/cron.log 2>&1

Parte 13. Consultas útiles para revisar la información

Ver todas las lecturas

SELECT * FROM lecturas_sensores;

Ver las últimas lecturas

SELECT * FROM lecturas_sensores ORDER BY fecha_hora DESC;

Ver solo lecturas de una Raspberry

SELECT * FROM lecturas_sensores
WHERE raspberry_id = 'RPI01';

Ver solo lecturas en estado crítico

SELECT * FROM lecturas_sensores
WHERE estado_alerta = 'critico';

Ver solo lecturas pendientes de enviar al servidor central

SELECT * FROM lecturas_sensores
WHERE enviado_central = 0;

Contar cuántas lecturas no se han enviado todavía

SELECT COUNT(*) AS pendientes
FROM lecturas_sensores
WHERE enviado_central = 0;