Sintetizador granular portátil con Raspberry Pi Pico y OLED

por | agosto 8, 2026
A DIY portable granular synthesizer with Raspberry Pi Pico, OLED screen, knobs, and 3D printed case on a table, realistic photo

Construir un sintetizador granular portátil puede sonar complejo, pero con la Raspberry Pi Pico y CircuitPython es un proyecto accesible incluso para aficionados con conocimientos básicos de electrónica. En esta guía te llevo paso a paso desde cero: soldar los componentes, cargar el código para la síntesis granular en tiempo real, diseñar e imprimir la carcasa 3D y hasta cómo cargar samples desde una tarjeta SD. Al final tendrás un chisme que cabe en tu bolsillo y suena gloriosamente glitchy.

¿Qué es la síntesis granular y por qué mola tanto?

La síntesis granular parte un sonido en trocitos minúsculos llamados granos (normalmente de entre 10 y 100 ms) y después los reproduce superpuestos, con variaciones de tono, posición y densidad. El resultado va desde drones etéreos hasta texturas rítmicas imposibles. Para nuestro proyecto usaremos un microcontrolador barato pero potente: la Raspberry Pi Pico, con su procesador dual-core capaz de manejar audio en tiempo real si optimizamos el código.

Materiales necesarios

  • Raspberry Pi Pico (con o sin wifi, la normal vale)
  • Pantalla OLED SSD1306 de 0.96″ (I2C)
  • Módulo de tarjeta microSD (SPI)
  • DAC de audio I2S (recomendado: PCM5102 o similar)
  • Potenciómetros y botones para controlar parámetros
  • Protoboard o placa perforada, cables dupont
  • Micro SD (FAT32) con samples en WAV mono 16-bit
  • Fuente de alimentación recargable (power bank pequeño o batería LiPo con cargador)

Paso 1: Cableado y soldadura

No hay que asustarse: las conexiones son pocas. La pantalla OLED va a los pines I2C1 (GP2 SDA, GP3 SCL). El módulo SD usa SPI1 (GP10 SCK, GP11 MOSI, GP12 MISO, GP13 CS). Y el DAC I2S necesita tres pines: BCK (GP16), LRCK (GP17), DIN (GP18). Los potenciómetros los conectas a entradas analógicas ADC (GP26, GP27, GP28) y los botones a GPIO con resistencias pull-up. Si no te quieres complicar con el DAC, puedes usar la salida PWM de la Pico con un filtro RC simple, pero la calidad será menor. Te animo a usar el I2S para un sonido limpio.

Sintetizador granular portátil con Raspberry Pi Pico y OLED

Solda con cuidado los pines a una placa perforada o diseña una PCB sencilla. Yo usé una placa de prototipado pequeño y quedó compacto.

Paso 2: Instalar CircuitPython en la Pico

CircuitPython es nuestro sistema operativo. Descarga el UF2 de circuitpython.org para Raspberry Pi Pico, mantén presionado el botón BOOTSEL al conectarla por USB y arrastra el firmware a la unidad RPI-RP2. Después copia las librerías necesarias: adafruit_ssd1306, adafruit_sdcard, adafruit_bus_device, adafruit_register. Con eso ya podrás empezar a cacharrear.

Paso 3: Cargar samples desde la SD

La Pico tiene poco espacio, así que los samples largos van en la microSD. Formatea la tarjeta en FAT32 y mete archivos WAV (mono, 16 bits, frecuencia de muestreo 22050 Hz o inferior). Aquí te dejo el código para leer un archivo WAV y almacenar sus datos en un buffer de la memoria RAM.


import board
import busio
import digitalio
import sdcardio
import storage

# Configurar SPI para SD
spi = busio.SPI(board.GP10, MOSI=board.GP11, MISO=board.GP12)
cs = digitalio.DigitalInOut(board.GP13)
sd = sdcardio.SDCard(spi, cs)
vfs = storage.VfsFat(sd)
storage.mount(vfs, "/sd")

# Leer archivo WAV (ejemplo simple asumiendo formato conocido)
with open("/sd/sample.wav", "rb") as f:
    # Saltar cabecera WAV y leer datos crudos
    f.seek(44)
    raw_data = f.read()
    # Convertir a array de enteros de 16 bits
    samples = memoryview(raw_data).cast('h', shape=(len(raw_data)//2,))

Ojo: esto asume un WAV sin comprimir con cabecera de 44 bytes. Para una implementación sólida deberías parsear el chunk ‘data’.

Paso 4: Síntesis granular en tiempo real

Aquí está el núcleo del proyecto. La idea es mantener una nube de granos activos y para cada frame de audio generar la suma de las muestras correspondientes. Como la Pico no tiene un sistema operativo multitarea real, todo el proceso debe ejecutarse en el bucle principal, con la ayuda de los timers de hardware para garantizar una tasa de muestreo fija. Aquí un esqueleto:


import time
import array
import math
from machine import Pin, I2S

# Configurar I2S
audio_out = I2S(0,
               sck=Pin(16),
               ws=Pin(17),
               sd=Pin(18),
               mode=I2S.TX,
               bits=16,
               format=I2S.MONO,
               rate=22050,
               ibuf=4096)

# Variables globales
grains = []               # Lista de granos activos
sample_buffer = samples    # Datos del sample leído previamente
sample_length = len(sample_buffer)

class Grain:
    def __init__(self, start_pos, speed, length, amp):
        self.position = start_pos
        self.speed = speed
        self.length = length
        self.amplitude = amp
        self.samples_remaining = length
        self.age = 0

    def get_next_value(self):
        if self.samples_remaining <= 0:
            return 0
        # Interpolación lineal simple
        idx = int(self.position)
        frac = self.position - idx
        if idx < sample_length - 1:
            val = sample_buffer[idx] * (1-frac) + sample_buffer[idx+1] * frac
        else:
            val = 0
        self.position += self.speed
        self.samples_remaining -= 1
        # Aplicar envolvente (ataque/caída para evitar clicks)
        env = 1.0
        if self.age < 100:
            env = self.age/100.0
        elif self.samples_remaining  32767: output[i] = 32767
        elif output[i] < -32768: output[i] = -32768
    return output

# Bucle principal
while True:
    # Leer potenciómetros para controlar parámetros
    # ... actualizar añadir/quitar granos, densidad, etc.
    block = generate_audio_block(64)
    audio_out.write(bytes(block))

Este código es la base. Para una experiencia más musical, necesitas controlar la creación de granos: cada cierto intervalo (densidad) creas un Grain aleatorio dentro de un rango de posición del sample, con una velocidad (pitch) y tamaño determinados. Los potenciómetros modificarán esos rangos.

Paso 5: Interfaz con pantalla OLED y controles

La pantalla SSD1306 muestra los parámetros actuales. Con cuatro botones y un encoder rotatorio puedes navegar por menús. La librería Adafruit es sencilla:


import board
import displayio
import adafruit_ssd1306

i2c = board.I2C()
oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
oled.fill(0)
oled.text("Grano: 1245", 0, 0, 1)
oled.text("Densidad: 32", 0, 10, 1)
oled.show()

Conecta los potenciómetros a ADC para parámetros como posición de inicio, tamaño de grano, densidad y pitch. En el bucle lees adc.value y mapeas a rangos útiles.

Paso 6: Diseño de la carcasa 3D

En Fusion 360 o TinkerCAD diseña una caja con espacio para todos los módulos. Mide los componentes con calibre y recuerda dejar aberturas para la pantalla, potes, botones, USB y SD. Exporta a STL e imprime en PLA. Puedes añadir una tapa deslizante para acceder a la SD. Aquí tienes un diseño funcional que yo he usado: (enlace a Thingiverse o descripción).

Consejos finales y variaciones

Una vez montado, tocará ajustar parámetros al gusto. La síntesis granular es muy sensible: con densidades bajas consigues rítmicas punteadas; con densidades altas, nubes densas. Prueba diferentes samples: voces, cuerdas, ruido blanco. Si quieres añadir reverberación, puedes añadir un pequeño algoritmo de reverby en el bucle de audio.

Y si en algún momento necesitas convertir tus samples favoritos a WAV o a la frecuencia de muestreo correcta, hay herramientas online que facilitan el proceso. Por ejemplo, en y-mp3.top puedes convertir archivos de audio entre muchos formatos sin perder calidad.

Ahora, ¡manos a la obra! Este proyecto aúna electrónica, programación y diseño 3D. El resultado es un aparato personal y único, con un sonido que no encontrarás en ningún plugin comercial.

Preguntas frecuentes

¿Puedo usar una Raspberry Pi normal (no Pico)?

Sí, pero con Raspberry Pi 4 puedes ejecutar Python completo y usar librerías como Csound o SuperCollider, aunque perderías portabilidad extrema y bajo consumo.

¿Es posible añadir entrada de línea o micrófono?

Con un ADC de audio como el INMP441 puedes capturar sonido en tiempo real y granularlo, pero la Pico se quedaría corta de memoria para procesos largos.

¿Hay alternativas a CircuitPython?

MicroPython o C puro con el SDK de Pico te darían más rendimiento, pero CircuitPython es genial para prototipado rápido.