Caso práctico: contador de pulsos con Raspberry Pi

Caso práctico: monitor consumo con Raspberry Pi — hero

Objetivo y caso de uso

Lo que construirás: Un registrador de pulsos de contador de suministros para Raspberry Pi que lee una salida de pulsos segura de bajo voltaje en un pin GPIO, elimina el rebote de cada evento y escribe marcas de tiempo, totales de pulsos y consumo calculado en un archivo CSV local. Está diseñado para pulsos LED de electricidad, interruptores reed de gas o salidas de contacto de agua, con una precisión temporal práctica en el rango de milisegundos y una carga del sistema muy baja, normalmente <5% de CPU y uso de GPU despreciable.

Por qué importa / Casos de uso

  • Hacer seguimiento del consumo eléctrico de una casa o taller a partir de una constante del contador como 1000 pulsos/kWh y estimar la demanda casi en tiempo real a partir del espaciado entre pulsos.
  • Registrar el consumo de gas o agua desde salidas reed/de contacto para informes de uso diario, detección de fugas o comparación entre ocupación y uso de suministros.
  • Crear una configuración simple de monitorización sin conexión y sin dependencia de la nube, usando datos CSV para hojas de cálculo, paneles o importación posterior a Home Assistant/Grafana.
  • Validar cambios de ahorro energético comparando tasas de pulsos antes y después de sustituir electrodomésticos, iluminación o programaciones de bombas.

Resultado esperado

  • Un contador de pulsos fiable que ignora el rebote del interruptor y los falsos disparos, con una latencia de gestión de eventos normalmente inferior a 10 ms para pulsos de contador de baja frecuencia.
  • Un registro CSV que contiene marca de tiempo, conteo de pulsos y unidades convertidas como kWh, m³ o litros según la constante de tu contador.
  • Un prototipo local funcional adecuado para tasas de pulsos muy por debajo de los límites GPIO de Raspberry Pi, sin requisito de FPS y con 0% de dependencia de GPU.
  • Una base reutilizable para añadir alertas, gráficos o resúmenes periódicos una vez verificado el registro básico.

Audiencia: Principiantes de Raspberry Pi, makers y propietarios de viviendas que crean monitorización básica de suministros; Nivel: Básico

Arquitectura/flujo: Salida de pulsos del contador o sensor óptico → entrada GPIO de Raspberry Pi con lógica antirrebote → marcado temporal de eventos de pulso → cálculo de consumo a partir de la constante de pulsos → registro en archivo CSV

Nota educativa de validación

Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. El validador comprobó los bloques de código, la estructura del artículo, los comandos copiables y la coherencia con el catálogo de dispositivos soportados.

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 3 apartados, 2 tablas y 11 bloques de código detectados antes de publicar.
  • Código comprobado: 3 Python/py_compile, 1 bloques de configuración/servicio, 6 Bash/copy-paste checks.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y los stacks no soportados bloquean la publicación.
  • Hallazgos del informe: sin hallazgos bloqueantes.

Esta validación confirma compatibilidad sintáctica y de herramientas para el material publicado, pero no sustituye la prueba física sobre tu hardware, cableado y entorno exactos.

Nota educativa de seguridad

Este proyecto es un prototipo educativo, no un producto certificado. Antes de alimentar la configuración, verifica el pinout de la revisión exacta de tu placa ULX3S, mantén las señales de E/S del FPGA a 3.3 V, nunca conectes 5 V directamente a los pines de E/S, desconecta la alimentación antes de cambiar el cableado y usa fuentes externas adecuadas para cargas, motores o servos compartiendo tierra solo cuando el cableado lo requiera.


title: Caso práctico: Registrador de pulsos de contador de suministros para Raspberry Pi
slug: practical-case-raspberry-pi
level: Basic


Registrador de pulsos de contador de suministros para Raspberry Pi

Este caso práctico muestra cómo construir un registrador de pulsos de contador de suministros en una Raspberry Pi. El registrador cuenta pulsos desde una salida de contador de suministros como:

  • una salida de pulsos LED de contador eléctrico
  • una salida de pulsos de interruptor reed de contador de gas
  • un contacto de pulsos de contador de agua

El resultado es un registro CSV local con marcas de tiempo, conteos de pulsos y consumo calculado según la constante de tu contador.

Nota de seguridad

Los contadores de suministros pueden estar conectados a equipos de red peligrosos. No abras, modifiques ni inspecciones el interior de una carcasa de contador de suministros. Usa solo una salida de pulsos de bajo voltaje proporcionada por el fabricante, un sensor óptico de pulsos aprobado o una salida reed de contacto seco. Si no estás seguro de que la señal sea de voltaje extrabajo y esté aislada, no la conectes a una Raspberry Pi.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

Salida de pulsos del contador o sensor óp…

entrada GPIO de Raspberry Pi con lógica a…

marcado temporal de eventos de pulso

cálculo de consumo a partir de la constan…

registro en archivo CSV

Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.

Ruta de validación

Código fuente

PlatformIO build

Flash

Monitor serie

Resumen conceptual de las herramientas usadas para comprobar el material publicado.

Objetivo y caso de uso

Construir un registrador de pulsos de contador de suministros que:

  1. lea una entrada de pulsos en un pin GPIO de Raspberry Pi
  2. elimine el rebote de la señal
  3. almacene eventos de pulsos en un archivo CSV
  4. calcule el consumo a partir de una constante de pulsos conocida del contador

Ejemplos de constantes de pulsos:

  • contador eléctrico: 1000 pulses/kWh
  • contador de agua: 1 pulse/litre
  • contador de gas: depende del modelo del contador

Hardware

  • Raspberry Pi con cabecera de 40 pines
  • tarjeta microSD con Raspberry Pi OS
  • fuente de pulsos del contador de suministros:
  • interruptor reed de contacto seco, o
  • salida de colector abierto / optoacoplador, o
  • sensor óptico de pulsos con salida aislada
  • cables jumper
  • resistencia externa opcional si tu sensor la requiere

Cableado

Este ejemplo usa GPIO17 como entrada de pulsos y la resistencia pull-up interna de Raspberry Pi. El sensor debe llevar el pin GPIO a GND brevemente por cada pulso.

Pin de Raspberry PiBCM GPIOConecta aNotas
Pin 11GPIO17Señal de pulsos del contadorEntrada de pulsos activa en bajo
Pin 6GNDTierra / retorno de pulsos del contadorReferencia común necesaria para contacto seco
3V3No se usa en este ejemplo básico de contacto secoNo apliques 5 V al GPIO

Cableado de pulsos por contacto seco

Usa esto si el contador de suministros proporciona un interruptor reed o contacto seco:

  • un contacto -> GPIO17
  • otro contacto -> GND

Cuando el contacto se cierra, la entrada pasa a nivel bajo y se cuenta un pulso.

Cableado de pulsos por colector abierto

Usa esto solo si la salida es compatible con 3.3 V y comparte una tierra segura de bajo voltaje:

  • colector/salida -> GPIO17
  • emisor/tierra -> GND

La pull-up interna mantiene la línea en alto cuando está inactiva.

Configuración del software

Actualiza el sistema e instala soporte GPIO para Python.

sudo apt update
sudo apt install -y python3-rpi.gpio
mkdir -p ~/utility-meter-logger
cd ~/utility-meter-logger

Registrador en Python

Crea meter_logger.py:

#!/usr/bin/env python3
import csv
import os
import signal
import sys
import time
from datetime import datetime, timezone

import RPi.GPIO as GPIO

GPIO_PIN = 17
CSV_PATH = os.path.expanduser("~/utility-meter-logger/meter_log.csv")
STATE_PATH = os.path.expanduser("~/utility-meter-logger/state.txt")

# Example: 1000 pulses per kWh for an electricity meter.
# Change this value to match your utility meter.
PULSES_PER_UNIT = 1000.0
UNIT_NAME = "kWh"

# Debounce time in milliseconds. Increase if your contact bounces.
BOUNCE_MS = 50

running = True
pulse_count = 0


def load_state(path: str) -> int:
    if not os.path.exists(path):
        return 0
    with open(path, "r", encoding="utf-8") as f:
        text = f.read().strip()
    if text == "":
        return 0
    return int(text)


def save_state(path: str, count: int) -> None:
    tmp_path = path + ".tmp"
    with open(tmp_path, "w", encoding="utf-8") as f:
        f.write(f"{count}\n")
    os.replace(tmp_path, path)


def ensure_csv_header(path: str) -> None:
    if os.path.exists(path) and os.path.getsize(path) > 0:
        return
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "a", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(
            ["timestamp_utc", "pulse_count_total", "delta_pulses", "consumption_total", "unit"]
        )


def append_csv(path: str, count: int, delta: int) -> None:
    consumption = count / PULSES_PER_UNIT
    timestamp = datetime.now(timezone.utc).isoformat()
    with open(path, "a", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow([timestamp, count, delta, f"{consumption:.6f}", UNIT_NAME])


def handle_pulse(channel: int) -> None:
    global pulse_count
    pulse_count += 1
    save_state(STATE_PATH, pulse_count)
    append_csv(CSV_PATH, pulse_count, 1)
    print(
        f"{datetime.now(timezone.utc).isoformat()} pulse={pulse_count} "
        f"consumption={pulse_count / PULSES_PER_UNIT:.6f} {UNIT_NAME}",
        flush=True,
    )


def handle_signal(signum, frame) -> None:
    global running
    running = False


def main() -> int:
    global pulse_count

    pulse_count = load_state(STATE_PATH)
    ensure_csv_header(CSV_PATH)

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    signal.signal(signal.SIGINT, handle_signal)
    signal.signal(signal.SIGTERM, handle_signal)

    GPIO.add_event_detect(
        GPIO_PIN,
        GPIO.FALLING,
        callback=handle_pulse,
        bouncetime=BOUNCE_MS,
    )

    print(
        f"Utility meter pulse logger started on GPIO{GPIO_PIN}. "
        f"Current total pulses: {pulse_count}",
        flush=True,
    )

    try:
        while running:
            time.sleep(1.0)
    finally:
        GPIO.cleanup()

    return 0


if __name__ == "__main__":
    sys.exit(main())

Hazlo ejecutable:

chmod +x ~/utility-meter-logger/meter_logger.py

Ejecutar el registrador de pulsos del contador de suministros

Inícialo manualmente para una primera prueba:

cd ~/utility-meter-logger
python3 meter_logger.py

Comportamiento esperado:

  • el script imprime un mensaje de inicio
  • cada pulso válido imprime una nueva línea
  • se crea meter_log.csv
  • state.txt almacena el conteo total de pulsos para recuperación tras reinicio

Ejemplo de salida

Algunas líneas de la consola serie pueden verse así:

Utility meter pulse logger started on GPIO17. Current total pulses: 0
2026-06-27T12:00:01.100000+00:00 pulse=1 consumption=0.001000 kWh
2026-06-27T12:00:03.700000+00:00 pulse=2 consumption=0.002000 kWh
2026-06-27T12:00:04.900000+00:00 pulse=3 consumption=0.003000 kWh

Formato CSV

El registrador escribe una fila por cada evento de pulso.

ColumnaSignificado
timestamp_utcMarca de tiempo UTC del pulso
pulse_count_totalConteo total acumulado de pulsos
delta_pulsesPulsos añadidos por este evento, normalmente 1
consumption_totalpulse_count_total / PULSES_PER_UNIT
unitUnidad de consumo, por ejemplo kWh o litre

Iniciar automáticamente al arrancar

Crea un archivo de servicio systemd en /etc/systemd/system/utility-meter-logger.service:

[Unit]
Description=Utility Meter Pulse Logger
After=network.target

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/utility-meter-logger
ExecStart=/usr/bin/python3 /home/pi/utility-meter-logger/meter_logger.py
Restart=always
RestartSec=2

[Install]
WantedBy=multi-user.target

Habilita e inicia el servicio:

sudo systemctl daemon-reload
sudo systemctl enable utility-meter-logger.service
sudo systemctl start utility-meter-logger.service
sudo systemctl status utility-meter-logger.service --no-pager

Método de validación

Para validar que este registrador de pulsos de contador de suministros funciona correctamente, usa una fuente de pulsos conocida.

Método 1: Prueba manual de contacto seco

  1. Detén el servicio si está en ejecución:
   sudo systemctl stop utility-meter-logger.service
   ```

2. Ejecuta el registrador en una terminal:

```bash
   cd ~/utility-meter-logger
   python3 meter_logger.py
   ```

3. Cortocircuita brevemente `GPIO17` con `GND` exactamente 10 veces usando un pulsador o un cable jumper.
4. Comprueba que se imprimen 10 líneas nuevas y que el conteo total de pulsos aumenta en 10.

Evidencia esperada:

- la salida de consola incrementa una vez por cada cortocircuito
- `tail -n 10 ~/utility-meter-logger/meter_log.csv` muestra 10 filas nuevas
- `cat ~/utility-meter-logger/state.txt` coincide con el conteo total esperado

### Método 2: Comprobación de la constante del contador

Si tu contador eléctrico está etiquetado como `1000 imp/kWh` o `1000 pulses/kWh`, entonces:

- 1000 pulsos registrados deben equivaler a `1.000000 kWh`
- 100 pulsos deben equivaler a `0.100000 kWh`

Evidencia esperada:

- el campo `consumption_total` en el CSV coincide con `pulse_count_total / 1000`

## Solución de problemas

### No se cuentan pulsos

Comprueba:

- que la salida del sensor realmente lleva la línea a GND
- que el número GPIO es correcto: `GPIO17`, no el número de pin físico `17`
- que la salida del contador es segura y de bajo voltaje
- que el tiempo antirrebote no es demasiado corto para un contacto reed

### Conteo doble

Si ves conteos extra desde un interruptor reed, aumenta:


```python
BOUNCE_MS = 100

Luego reinicia el script.

Unidades de consumo incorrectas

Configura correctamente estos valores para tu contador de suministros:

PULSES_PER_UNIT = 1000.0
UNIT_NAME = "kWh"

Ejemplos:

  • contador de agua con 1 pulse/litre:
  • PULSES_PER_UNIT = 1.0
  • UNIT_NAME = "litre"
  • contador eléctrico con 1000 pulses/kWh:
  • PULSES_PER_UNIT = 1000.0
  • UNIT_NAME = "kWh"

Resultado

Ahora tienes un registrador de pulsos de contador de suministros para Raspberry Pi que registra eventos de pulso, conserva el conteo total entre reinicios y almacena datos de consumo con marca de tiempo en formato CSV.

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Cuál es el objetivo principal del sistema descrito en el artículo?




Pregunta 2: ¿Qué dispositivo se utiliza como base para construir el registrador de pulsos?




Pregunta 3: ¿Qué tipo de señal lee el sistema en un pin GPIO?




Pregunta 4: ¿Qué problema intenta evitar el sistema en cada evento de pulso?




Pregunta 5: ¿En qué formato se almacenan localmente los datos registrados?




Pregunta 6: ¿Qué constante de contador se menciona como ejemplo para seguir el consumo eléctrico?




Pregunta 7: Además de electricidad, ¿qué otros suministros puede registrar este sistema?




Pregunta 8: ¿Qué datos escribe el sistema en el archivo CSV?




Pregunta 9: ¿Qué ventaja tiene esta configuración respecto a la conectividad?




Pregunta 10: ¿Qué carga del sistema se indica normalmente para esta solución?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:
Scroll al inicio