Nota educativa de seguridad: Este proyecto está diseñado con fines educativos para demostrar el procesamiento de audio I2S y el control PWM. Proporciona retroalimentación visual complementaria, pero no debe reemplazar el equipo de seguridad industrial certificado, el monitoreo de ruido compatible con OSHA o los protocolos de protección auditiva obligatorios en entornos peligrosos.
Objetivo y caso de uso
- Qué construirás: Construirás un semáforo de ruido de taller automatizado y en tiempo real utilizando un micrófono digital I2S y un anillo LED RGB programable.
- Por qué es importante / Casos de uso:
- Espacios de creación compartidos (Makerspaces): Recuerda visualmente a los usuarios que usen protección auditiva cuando maquinaria pesada (como enrutadores CNC o sierras de mesa) esté en funcionamiento.
- Gestión del aula: Proporciona retroalimentación objetiva y no verbal a los estudiantes cuando el volumen del trabajo en grupo supera los niveles aceptables.
- Plantas de fábrica: Actúa como un monitor de ruido ambiental localizado y de bajo costo para registrar las condiciones ambientales y activar advertencias visuales localizadas.
- Bibliotecas / Zonas de estudio: Puede ajustarse a umbrales altamente sensibles para mantener zonas de silencio estrictas sin intervención humana.
- Resultado esperado:
- Un servicio basado en Python que captura continuamente datos de audio de modulación por impulsos codificados (PCM) a través de la interfaz I2S.
- Conversión matemática en tiempo real de fragmentos de audio sin procesar a la raíz cuadrada media (RMS) y decibelios relativos a la escala completa (dBFS).
- Cambios de estado visuales inmediatos en el anillo NeoPixel: Verde (silencioso), Amarillo (advertencia/moderado) y Rojo (ruidoso).
- Registro en consola de valores numéricos de dBFS para calibración y seguimiento ambiental.
- Audiencia y nivel: Estudiantes de electrónica básica e ingeniería informática.
- Arquitectura/flujo:
INMP441 (I2S Audio) -> Raspberry Pi 4B (Python/RMS Math) -> WS2812B NeoPixel Ring (PWM).
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: 15 apartados, 1 tablas y 2 bloques de código detectados antes de publicar.
- Código comprobado: 1 Python/py_compile, 1 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 de bajo voltaje, no un producto certificado. Antes de encender la configuración, verifique la distribución de pines (pinout) de su Raspberry Pi exacta, nunca conecte 5 V a pines GPIO de 3.3 V, desconecte la alimentación antes de cambiar el cableado y use interfaces o fuentes de alimentación externas adecuadas para sensores, relés, motores o cargas.
Requisitos previos
- Hardware: Raspberry Pi 4 Model B (o Raspberry Pi 5).
- Sistema Operativo: Raspberry Pi OS Bookworm 64-bit.
- Software: Python 3.11.
- Paquetes del sistema:
python3-pip,python3-venv,portaudio19-devygit. - Privilegios: El usuario debe ser miembro de los grupos
audio,gpioyvideopara acceder al hardware I2S y a los periféricos PWM sin privilegios de root.
Materiales
- Computación: Raspberry Pi 4 Model B (se recomiendan 4GB o 8GB de RAM).
- Micrófono: Módulo de micrófono digital omnidireccional I2S INMP441.
- Indicador visual: Anillo de estado NeoPixel WS2812B (variante de 12 o 16 LED).
- Alimentación: Fuente de alimentación oficial USB-C de 15W (5V/3A) para Raspberry Pi.
- Cableado: Cables puente (jumper) hembra-hembra (al menos 9 cables).
Configuración / Conexión
Conectar un micrófono I2S y un anillo NeoPixel simultáneamente requiere una cuidadosa selección de pines. El protocolo I2S utiliza pines de hardware específicos para su reloj en serie (BCLK), selección de palabra (LRCLK) y datos en serie (DIN). En la Raspberry Pi 4 Model B, el pin de reloj I2S predeterminado es el GPIO 18.
Debido a que los NeoPixels WS2812B requieren una sincronización estricta típicamente generada por el hardware PWM de la Pi, y dado que PWM0 también está asignado al GPIO 18 por defecto, tenemos un conflicto de hardware. Para resolver esto, enrutamos la línea de datos del NeoPixel al GPIO 12, lo que proporciona acceso al PWM0 sin interferir con el reloj I2S en el GPIO 18.
Tabla de asignación de pines
| Componente | Pin del componente | Pin de Raspberry Pi 4B | N.º de pin físico | Nota |
|---|---|---|---|---|
| INMP441 | VDD | Alimentación 3.3V | Pin 1 | No use 5V para el INMP441. |
| INMP441 | GND | Tierra (Ground) | Pin 9 | Tierra común. |
| INMP441 | L/R | Tierra (Ground) | Pin 14 | Conecta el micrófono al canal I2S izquierdo. |
| INMP441 | WS | GPIO 19 (PCM_FS) | Pin 35 | Word Select / Reloj Izquierda-Derecha. |
| INMP441 | SCK | GPIO 18 (PCM_CLK) | Pin 12 | Reloj en serie / Reloj de bits. |
| INMP441 | SD | GPIO 20 (PCM_DIN) | Pin 38 | Entrada de datos en serie a la Pi. |
| NeoPixel | VCC | Alimentación 5V | Pin 2 | Los NeoPixels requieren lógica/alimentación de 5V. |
| NeoPixel | GND | Tierra (Ground) | Pin 6 | Tierra común. |
| NeoPixel | DIN | GPIO 12 (PWM0) | Pin 32 | Evita el conflicto con I2S en el GPIO 18. |
Habilitar I2S en el sistema operativo
Por defecto, la interfaz I2S está deshabilitada en Raspberry Pi OS. Debe habilitarla editando el archivo de configuración de arranque.
Abra /boot/firmware/config.txt (o /boot/config.txt en versiones anteriores) y agregue la siguiente línea en la parte inferior:
dtparam=i2s=on
Además, para cargar el controlador de audio I2S genérico, asegúrese de que esta línea esté presente:
dtoverlay=googlevoicehat-soundcard
Después de modificar este archivo, la Raspberry Pi debe reiniciarse.
Método de validación
Para validar la precisión matemática del cálculo de dBFS y la capacidad de respuesta del hardware, coloque un medidor de SPL (Nivel de Presión Sonora) comercial calibrado junto al micrófono INMP441. Reproduzca un tono de prueba constante de 1 kHz desde un altavoz a un volumen fijo.
Evidencia esperada: El registro de la consola debería generar un valor de dBFS estable con una variación de +/- 1.0 dBFS. Ajuste los argumentos --threshold-red y --threshold-yellow para que coincidan con los requisitos de referencia de su entorno.
Código de implementación
La siguiente sección contiene dos scripts. El primero es un script de Bash para automatizar la instalación de dependencias y la configuración del sistema. El segundo es la aplicación principal en Python. La aplicación de Python incluye clases de adaptadores simulados (mock), lo que permite validar el código en una PC estándar (modo de prueba en seco) sin hardware GPIO o I2S físico conectado.
1. Script de configuración del entorno (setup_env.sh)
#!/bin/bash
echo "Updating package lists..."
sudo apt-get update
echo "Installing system dependencies..."
sudo apt-get install -y portaudio19-dev python3-pyaudio python3-pip python3-venv
echo "Creating Python virtual environment..."
python3 -m venv workshop_env
source workshop_env/bin/activate
echo "Installing Python libraries..."
pip install pyaudio rpi_ws281x adafruit-circuitpython-neopixel
echo "Adding user to required hardware groups..."
sudo usermod -a -G audio,gpio,video $USER
echo "Setup complete. Please reboot your Raspberry Pi to apply group changes."
echo "After reboot, activate the environment using: source workshop_env/bin/activate"
2. Aplicación principal (noise_traffic_light.py)
Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.
#!/usr/bin/env python3
"""
noise_traffic_light.py
Monitors ambient noise via an I2S microphone and controls a NeoPixel ring.
Includes a dry-run mode for educational validation on standard PCs.
"""
import sys
import math
import struct
import time
import argparse
import logging
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Hardware libraries (wrapped in try-except for dry-run compatibility)
try:
import pyaudio
HAS_PYAUDIO = True
except ImportError:
HAS_PYAUDIO = False
try:
import board
import neopixel
HAS_NEOPIXEL = True
except ImportError:
HAS_NEOPIXEL = False
class MockAudioStream:
"""Generates synthetic PCM audio data to simulate a fluctuating noise environment."""
def __init__(self, chunk_size, rate):
self.chunk_size = chunk_size
self.rate = rate
self.start_time = time.time()
def read(self, num_frames, exception_on_overflow=False):
# Simulate elapsed time
time.sleep(num_frames / self.rate)
t = time.time() - self.start_time
# Create a pulsing envelope: goes from 10% to 100% of max amplitude over 10 seconds
envelope = 0.1 + 0.9 * ((math.sin(t * 2 * math.pi / 10) + 1) / 2)
max_amp = 32767 * envelope
data = bytearray()
for i in range(num_frames):
# Generate a 440Hz sine wave sample
sample = int(max_amp * math.sin(2 * math.pi * 440 * (i / self.rate)))
# Pack as 16-bit signed integer (little-endian)
data.extend(struct.pack('<h', sample))
return bytes(data)
def close(self):
pass
class MockStatusRing:
"""Simulates a NeoPixel ring by printing colors to the console."""
def __init__(self, num_pixels):
self.num_pixels = num_pixels
self.current_color = None
def fill(self, color):
if color != self.current_color:
self.current_color = color
logging.info(f"[MOCK LED] Ring color changed to RGB: {color}")
class RealAudioStream:
"""Wrapper for the PyAudio I2S stream."""
def __init__(self, chunk_size, rate):
self.pa = pyaudio.PyAudio()
self.stream = self.pa.open(
format=pyaudio.paInt16,
channels=1,
rate=rate,
input=True,
frames_per_buffer=chunk_size
)
def read(self, num_frames, exception_on_overflow=False):
return self.stream.read(num_frames, exception_on_overflow=exception_on_overflow)
def close(self):
self.stream.stop_stream()
self.stream.close()
self.pa.terminate()
# ...#!/usr/bin/env python3
"""
noise_traffic_light.py
Monitors ambient noise via an I2S microphone and controls a NeoPixel ring.
Includes a dry-run mode for educational validation on standard PCs.
"""
import sys
import math
import struct
import time
import argparse
import logging
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Hardware libraries (wrapped in try-except for dry-run compatibility)
try:
import pyaudio
HAS_PYAUDIO = True
except ImportError:
HAS_PYAUDIO = False
try:
import board
import neopixel
HAS_NEOPIXEL = True
except ImportError:
HAS_NEOPIXEL = False
class MockAudioStream:
"""Generates synthetic PCM audio data to simulate a fluctuating noise environment."""
def __init__(self, chunk_size, rate):
self.chunk_size = chunk_size
self.rate = rate
self.start_time = time.time()
def read(self, num_frames, exception_on_overflow=False):
# Simulate elapsed time
time.sleep(num_frames / self.rate)
t = time.time() - self.start_time
# Create a pulsing envelope: goes from 10% to 100% of max amplitude over 10 seconds
envelope = 0.1 + 0.9 * ((math.sin(t * 2 * math.pi / 10) + 1) / 2)
max_amp = 32767 * envelope
data = bytearray()
for i in range(num_frames):
# Generate a 440Hz sine wave sample
sample = int(max_amp * math.sin(2 * math.pi * 440 * (i / self.rate)))
# Pack as 16-bit signed integer (little-endian)
data.extend(struct.pack('<h', sample))
return bytes(data)
def close(self):
pass
class MockStatusRing:
"""Simulates a NeoPixel ring by printing colors to the console."""
def __init__(self, num_pixels):
self.num_pixels = num_pixels
self.current_color = None
def fill(self, color):
if color != self.current_color:
self.current_color = color
logging.info(f"[MOCK LED] Ring color changed to RGB: {color}")
class RealAudioStream:
"""Wrapper for the PyAudio I2S stream."""
def __init__(self, chunk_size, rate):
self.pa = pyaudio.PyAudio()
self.stream = self.pa.open(
format=pyaudio.paInt16,
channels=1,
rate=rate,
input=True,
frames_per_buffer=chunk_size
)
def read(self, num_frames, exception_on_overflow=False):
return self.stream.read(num_frames, exception_on_overflow=exception_on_overflow)
def close(self):
self.stream.stop_stream()
self.stream.close()
self.pa.terminate()
class RealStatusRing:
"""Wrapper for the hardware NeoPixel ring."""
def __init__(self, pin, num_pixels):
# board.D12 corresponds to GPIO 12
self.pixels = neopixel.NeoPixel(pin, num_pixels, brightness=0.5, auto_write=True)
def fill(self, color):
self.pixels.fill(color)
def calculate_dbfs(pcm_data):
"""
Calculates the Decibels Relative to Full Scale (dBFS) of a PCM audio chunk.
0 dBFS is the maximum possible digital value. Normal audio is negative.
"""
# 16-bit audio = 2 bytes per frame
count = len(pcm_data) // 2
if count == 0:
return -96.0
# Unpack raw bytes into signed 16-bit integers
shorts = struct.unpack(f'<{count}h', pcm_data)
# Calculate Root Mean Square (RMS)
sum_squares = sum(s * s for s in shorts)
rms = math.sqrt(sum_squares / count)
if rms == 0:
return -96.0
# Calculate dBFS (Max RMS for 16-bit signed is approx 32768)
dbfs = 20 * math.log10(rms / 32768.0)
return dbfs
def main():
parser = argparse.ArgumentParser(description="Workshop Noise Traffic Light")
parser.add_argument("--dry-run", action="store_true", help="Run with mock hardware interfaces")
parser.add_argument("--threshold-yellow", type=float, default=-25.0, help="dBFS threshold for Yellow")
parser.add_argument("--threshold-red", type=float, default=-15.0, help="dBFS threshold for Red")
args = parser.parse_args()
# Configuration constants
CHUNK = 4096
RATE = 44100
NUM_PIXELS = 12
# Initialize Hardware / Mocks
if args.dry_run:
logging.info("Starting in DRY-RUN mode. Using mock hardware.")
audio = MockAudioStream(CHUNK, RATE)
ring = MockStatusRing(NUM_PIXELS)
else:
if not HAS_PYAUDIO or not HAS_NEOPIXEL:
logging.error("Hardware libraries missing. Run environment setup or use --dry-run.")
sys.exit(1)
logging.info("Starting in HARDWARE mode.")
audio = RealAudioStream(CHUNK, RATE)
ring = RealStatusRing(board.D12, NUM_PIXELS)
# Traffic Light Colors (R, G, B)
COLOR_GREEN = (0, 255, 0)
COLOR_YELLOW = (255, 255, 0)
COLOR_RED = (255, 0, 0)
logging.info("Listening for ambient noise... Press Ctrl+C to stop.")
try:
while True:
# 1. Capture audio chunk
pcm_data = audio.read(CHUNK, exception_on_overflow=False)
# 2. Process audio to dBFS
dbfs = calculate_dbfs(pcm_data)
# 3. Determine state and set visual indicator
if dbfs >= args.threshold_red:
state = "LOUD"
ring.fill(COLOR_RED)
elif dbfs >= args.threshold_yellow:
state = "WARNING"
ring.fill(COLOR_YELLOW)
else:
state = "QUIET"
ring.fill(COLOR_GREEN)
logging.info(f"Level: {dbfs:.2f} dBFS | State: {state}")
except KeyboardInterrupt:
logging.info("Stopping...")
finally:
audio.close()
if __name__ == "__main__":
main()
Encuentra este producto y/o libros sobre este tema en 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
Diagrama de bloques conceptual
Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.
Arquitectura funcional
Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.




