Tu ESP32 entrega 3,3 V. El variador de frecuencia que quieres controlar espera 0 a 10 V. El balasto del panel LED regulable, también. La válvula proporcional del sistema de riego, la fuente de laboratorio programable, el driver del motor de la cinta: todos hablan el mismo idioma analógico, y ninguno entiende PWM de 3,3 V.

Ese salto de rango es exactamente el problema que resuelve el módulo GP8403. Al terminar este tutorial vas a poder fijar por I2C, desde cualquier microcontrolador con dos cables, dos tensiones analógicas independientes entre 0 y 10 V con una precisión del 0,2 %, y vas a entender por qué esta solución no es reemplazable por el truco de PWM más filtro RC que se suele recomendar.

Por qué no basta con PWM y un condensador

Es la primera pregunta razonable: si el microcontrolador ya genera PWM, ¿por qué no filtrarlo y listo?

Por tres razones que se acumulan. La primera es de techo: un filtro RC promedia la señal PWM, así que la salida máxima es la tensión de alimentación del pin. Con 3,3 V de entrada nunca vas a llegar a 10 V por mucho que filtres; necesitas una etapa elevadora sí o sí. La segunda es el rizado: el filtro deja pasar una fracción de la frecuencia de conmutación, y para bajarla a niveles decentes hay que aumentar la constante de tiempo, lo que a su vez vuelve la respuesta más lenta. La tercera es la linealidad: la relación entre el ciclo de trabajo y la tensión filtrada depende de la carga conectada, así que el mismo código da tensiones distintas según qué tengas enchufado al otro lado.

El GP8403 (el chip se llama GP8402 y el módulo GP8403) resuelve las tres de una vez. Adentro tiene un conversor digital analógico de 12 bits que traduce linealmente valores de 0x000 a 0xFFF en dos salidas analógicas independientes, y un pequeño convertidor DC/DC en la propia placa que genera los 12 V necesarios para que esas salidas alcancen los 10 V reales. El error de tensión de salida es del 0,2 %, y no depende de la carga.

Lo que este módulo no es

Vale la pena decirlo temprano para que nadie compre el módulo equivocado: este DAC es para fijar niveles, no para generar formas de onda.

Las salidas llevan condensadores de 10 µF, tal como especifica la hoja de datos. Eso significa que hay un polo de baja frecuencia entre la salida del conversor y el mundo exterior: la tensión no salta instantáneamente al valor nuevo, sube hacia él. Para una consigna de velocidad de un variador, que de todas formas tiene su propia rampa de aceleración, es perfecto. Para sintetizar una senoidal de audio, no sirve; ahí necesitas otro tipo de conversor.

En la práctica el criterio es simple: si la tensión que quieres entregar cambia unas pocas veces por segundo o menos, este es tu módulo. Si cambia miles de veces por segundo, busca otra cosa.

Los pines que importan

La dirección I2C por defecto es 0x58.

Alimentación. El pin VCC alimenta la lógica del módulo y acepta de 3 a 5 V. La regla es darle la misma tensión que la lógica de tu microcontrolador: 3,3 V si trabajas con un ESP32, 5 V si es un Arduino UNO. El pin +12V es una salida, no una entrada: son los 12 V que genera el convertidor DC/DC de a bordo, y puedes sacar hasta 100 mA de ahí si necesitas alimentar algo externo. GND es la tierra común de alimentación y lógica.

Bus I2C. SCL es el reloj y SDA los datos, ambos con resistencias de pull up de 10 kΩ ya incorporadas y tolerantes a lógica de 3 a 5 V. Los conectores STEMMA QT de ambos lados son eléctricamente compatibles con los Qwiic de SparkFun, y sirven para conectar sin soldar y para encadenar el módulo con otros periféricos del mismo bus.

Salidas del DAC. OUT0/V0 y OUT1/V1 son las dos salidas analógicas, disponibles tanto en el borde inferior de la placa (OUT0, OUT1) como en la bornera de tornillo de 4 pines de 2,5 mm que viene ya soldada (V0, V1). Para cableado hacia un equipo real usa la bornera: los pines sueltos no aguantan la tracción de un cable largo.

Aritmética de direcciones

En la parte trasera hay tres puentes rotulados A0, A1 y A2. Soldándolos cerrados cambias la dirección I2C, lo que te permite encadenar hasta ocho módulos en el mismo par de cables: dieciséis salidas analógicas con dos pines del microcontrolador.

La fórmula es una suma binaria sobre la dirección base: A0 vale 1, A1 vale 2 y A2 vale 4.

dirección = 0x58 + A2 + A1 + A0

  • Solo A0 cerrado: 0x58 + 1 = 0x59
  • Solo A1 cerrado: 0x58 + 2 = 0x5A
  • Solo A2 cerrado: 0x58 + 4 = 0x5C
  • A2 y A0 cerrados: 0x58 + 4 + 1 = 0x5D
  • Los tres cerrados: 0x5F

Tabla con todas las direcciones I2C posibles del GP8403 y el estado alto o bajo de cada puente

Hay un cuarto puente, rotulado LED, en la parte de atrás a la derecha: cortando su pista apagas el LED verde de encendido. Útil si el módulo va dentro de una caja y no quieres una luz encendida las veinticuatro horas.

Conexiones

Son cuatro cables en todos los casos, y el patrón no cambia entre plataformas: alimentación, tierra, reloj y datos.

Para una placa de 5 V tipo Arduino UNO o Metro, conecta 5V de la placa a VIN del módulo, GND a GND, SCL a SCL y SDA a SDA. Para una placa de 3,3 V. un ESP32, un Feather. la única diferencia es que el primer cable sale del pin de 3,3 V en lugar del de 5 V.

Con conector STEMMA QT te ahorras los cuatro cables sueltos:

Placa Metro conectada al módulo GP8403 mediante el conector STEMMA QT

Y sobre protoboard, con los cuatro cables explícitos:

Placa Metro conectada al módulo GP8403 sobre una protoboard sin soldadura

En una placa de 3,3 V el esquema es idéntico salvo el pin de alimentación:

Feather RP2040 conectado al módulo GP8403 mediante el conector STEMMA

Y si el que manda es una Raspberry Pi, el cableado sale de los pines 3V, GND, SCL y SDA del cabezal:

Raspberry Pi conectada por I2C al módulo GP8403 mediante el conector STEMMA

La señal de salida se toma entre V0 de la bornera y GND, o entre el pin OUT0 y GND si trabajas sobre protoboard.

El error de cableado que arruina instalaciones reales

Acá va lo que el manual del módulo no te dice y la instalación sí. Una señal de 0 a 10 V es una tensión referida a tierra, no una señal diferencial. Eso implica dos cosas cuando el equipo controlado no está sobre tu mesa:

La tierra tiene que ser común. El GND del módulo y el GND de la entrada analógica del variador tienen que estar unidos. Si cada uno cuelga de su propia fuente sin referencia compartida, el equipo lee cualquier cosa. típicamente valores erráticos que suben y bajan solos. y vas a perder una tarde buscando un problema de software que no existe.

Los tramos largos necesitan cable apantallado. Una entrada analógica de alta impedancia junto a un variador de frecuencia es una antena excelente para el ruido de conmutación del propio variador. Sobre uno o dos metros no vas a notar nada; sobre diez metros de cable sin malla, junto a la potencia, la consigna te va a bailar. Usa par trenzado apantallado y conecta la malla a tierra en un solo extremo, el del controlador.

Código

CircuitPython y Python

La biblioteca es Adafruit_CircuitPython_GP8403. En un computador con Linux. una Raspberry Pi, por ejemplo. se instala junto a Blinka, la capa de compatibilidad que le da a Python normal la API de CircuitPython:

Bash
pip3 install adafruit-circuitpython-gp8403

Si tu Python por defecto es la versión 3, puede que tengas que escribir pip en lugar de pip3. CircuitPython no corre sobre Python 2.

En una placa con CircuitPython el procedimiento es distinto: copias la carpeta lib y el archivo code.py del bundle a la unidad CIRCUITPY. La carpeta CIRCUITPY/lib debe terminar conteniendo adafruit_bus_device/, adafruit_register/ y adafruit_gp8403.mpy.

El ejemplo mínimo:

Python
# SPDX-FileCopyrightText: Copyright (c) 2026 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import time

import board

import adafruit_gp8403

i2c = board.I2C()
# default 0-5V range:
dac = adafruit_gp8403.GP8403(i2c)
# for 0-10V range:
# dac = adafruit_gp8403.GP8403(i2c, output_range=adafruit_gp8403.Range.RANGE_10V)

dac.channel_0.voltage = 2.5  # set channel 0 to 2.5V
dac.channel_1.voltage = 3.5  # set channel 0 to 3.5V
while True:
    # can access voltages as a tuple too (dac.voltages):
    print(f"Channel 0: {dac.voltages[0]:.01f}V")
    print(f"Channel 1: {dac.voltages[1]:.01f}V")
    time.sleep(1)

Presta atención a la línea comentada: el rango de 0 a 10 V no es el que viene por defecto. Si la dejas comentada, el módulo trabaja en 0 a 5 V y vas a medir la mitad de lo que esperas. Es el error más común con este chip, y no da ningún síntoma más que un número bajo en el multímetro. Descoméntala cuando quieras el rango completo.

Arduino

Instala la biblioteca desde el gestor del IDE: menú Manage Libraries…, busca Adafruit_GP8403 y selecciona la biblioteca Adafruit GP8403.

Menú del IDE de Arduino con la opción Manage Libraries para abrir el gestor de bibliotecas

Gestor de bibliotecas del IDE de Arduino con la búsqueda de Adafruit_GP8403 y la biblioteca seleccionada

Si aparece la ventana de dependencias, elige "Install all". Si ya las tenías instaladas, la ventana no aparece. y en ese caso actualízalas desde el gestor antes de compilar el ejemplo, o vas a chocar con errores de compilación que parecen del código y son de versiones desalineadas.

C++
/*
 * Adafruit GP8403 Dual Output 5 V Example
 *
 * Selects the 0-5 V range and sets both output channels.
 */

#include <Adafruit_GP8403.h>

Adafruit_GP8403 gp8403;

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(10);
  }
  Serial.println("Adafruit GP8403 Dual Output 5 V Example");

  if (!gp8403.begin()) {
    Serial.println("Could not find GP8403");
    while (1) {
      delay(10);
    }
  }

  if (!gp8403.setOutputRange(GP8403_RANGE_5V)) {
    Serial.println("Could not select the 5 V output range");
    while (1) {
      delay(10);
    }
  }

  if (!gp8403.setVoltages(1.25, 3.75)) {
    Serial.println("Could not set both output voltages");
    while (1) {
      delay(10);
    }
  }

  // getVoltage() returns cached values, not hardware readback.
  Serial.print("Channel 0: ");
  Serial.print(gp8403.getVoltage(0), 2);
  Serial.println(" V");
  Serial.print("Channel 1: ");
  Serial.print(gp8403.getVoltage(1), 2);
  Serial.println(" V");
}

void loop() { delay(1000); }

Carga el sketch y abre el Monitor Serie (Herramientas → Monitor Serie) a 115200 baudios. Vas a ver el GP8403 reconocido por I2C.

Fíjate en el comentario de la línea de getVoltage(): ese método devuelve el valor que la biblioteca guardó en memoria, no una lectura del hardware. Es decir, no sirve para verificar que la salida realmente esté en 1,25 V; solo confirma qué le pediste. La verificación de verdad se hace con un instrumento.

De la magnitud física a la tensión

En un proyecto real casi nunca piensas en volts: piensas en "quiero el motor al 40 %" o "quiero el panel al 70 % de brillo". La conversión es una regla de tres, pero conviene escribirla bien una sola vez porque de acá salen la mitad de los errores de puesta en marcha.

Con el rango de 0 a 10 V seleccionado, un porcentaje se traduce así:

tensión = porcentaje / 100 × 10

Un 40 % son 4,0 V. Hasta ahí, trivial. Lo que rara vez es trivial es el otro extremo de la cadena: el equipo controlado casi nunca usa el rango completo. Un variador de frecuencia típico se configura con una frecuencia mínima. pongamos 20 Hz para que el motor no se quede sin ventilación. y una máxima de 50 Hz. En esa configuración, 0 V no significa "motor detenido" sino "motor a 20 Hz", y tu 40 % de consigna se convierte en 32 Hz reales, no en el 40 % de 50 Hz que tenías en la cabeza.

La forma correcta de plantearlo es partir de la magnitud física y bajar:

tensión = (valor_deseado − valor_mín) / (valor_máx − valor_mín) × 10

Para 32 Hz con un variador configurado entre 20 y 50 Hz: (32 − 20) / (50 − 20) × 10 = 4,0 V. Escribe esa función una vez en tu firmware, con los límites como constantes al principio del archivo, y el día que reconfigures el variador cambias dos números en lugar de perseguir factores mágicos repartidos por el código.

La resolución no va a ser tu problema: 12 bits sobre 10 V dan pasos de unos 2,4 mV, que sobre el rango de 30 Hz del ejemplo son incrementos de 0,007 Hz. Es varios órdenes de magnitud más fino que lo que cualquier variador puede seguir.

Comprobar que funciona

Conecta el multímetro entre V0 y GND, en modo tensión continua. Con el ejemplo de Arduino cargado deberías medir 1,25 V en la salida 0 y 3,75 V en la salida 1, dentro del 0,2 % de error.

Si mides aproximadamente la mitad de lo esperado, casi con seguridad estás en el rango de 0 a 5 V cuando querías el de 0 a 10 V. Si no mides nada, revisa en este orden: que el módulo aparezca en el escaneo I2C en 0x58, que VCC tenga la tensión de la lógica de tu placa, y que estés midiendo contra el GND del módulo y no contra otra tierra.

Un detalle que da confianza al depurar: como el chip tiene memoria no volátil, arranca en la última tensión que se le fijó. Eso es una ventaja en producción. el equipo controlado no salta a cero durante un reinicio del microcontrolador. pero también significa que una medición correcta puede venir de la configuración anterior y no de tu código nuevo. Cambia el valor a algo distinto para comprobar que efectivamente estás mandando la orden.

Sobre esa memoria no volátil hay una advertencia honesta del fabricante: se escribe con una técnica de bit banging sobre las líneas I2C. Funciona sobre un ATmega328, pero verifícala en tu plataforma antes de depender de ella, porque algunos microcontroladores no permiten tomar el control directo de los pines I2C que su periférico de hardware está gestionando.

Variantes y mejoras

Consigna de velocidad para un variador de frecuencia. Es la aplicación que justifica el módulo por sí sola. Conecta V0 a la entrada analógica del variador (habitualmente rotulada AI1 o VI) y su tierra al GND del módulo, configura el variador para consigna analógica de 0 a 10 V, y ya controlas la velocidad de un motor trifásico desde un ESP32. Con un potenciómetro en una entrada ADC del ESP32 y una regla de tres, tienes un mando manual; con MQTT, uno remoto.

Regulación de iluminación 0-10 V. Los balastos y drivers LED regulables industriales usan este mismo estándar. La segunda salida del módulo queda libre para un segundo circuito de luz, así que con un módulo controlas dos zonas independientes. Encadenando cuatro módulos por sus puentes de dirección llegas a ocho zonas con dos cables.

Banco de pruebas automatizado. Dos salidas independientes de 12 bits, controlables por I2C, son un generador de consignas para caracterizar circuitos: barre la tensión de polarización de un amplificador operacional en pasos de 2,4 mV mientras registras la respuesta, sin tocar nada a mano.

Encadenar hasta dieciséis salidas. Los tres puentes de dirección permiten ocho módulos sobre el mismo bus. Si el proyecto pide muchas consignas analógicas. una planta de riego por zonas, un banco de fuentes. , esta es la forma económica de escalar sin gastar pines.

Personalización para Chile

El catálogo automatizado de productos no estuvo disponible al generar este artículo, así que verifica precios y stock actualizados directamente en MechatronicStore. Los componentes que necesitas para reproducir el montaje:

  • Módulo DAC I2C de 0-10 V. el corazón del proyecto. Si el GP8403 específico no está disponible, revisa la familia GP8403/GP8503 de DFRobot, que es el mismo chip en otro formato de placa. Un MCP4725 no es equivalente: llega solo hasta la tensión de alimentación y no tiene elevador, que es justamente lo que estamos comprando acá.
  • Placa ESP32 DevKit V1. el controlador recomendado para la variante industrial de este tutorial, porque suma WiFi para la consigna remota. El tutorial original usa un Feather RP2040 y un Metro; cualquier placa con I2C sirve, y el ESP32 es la que mejor relación precio prestaciones tiene en el catálogo local.
  • Cable STEMMA QT / Qwiic de 4 pines. no viene incluido con el módulo. Si tu placa no tiene ese conector, reemplázalo por jumpers.
  • Protoboard 830 puntos y jumpers macho hembra de 20 cm. para el armado de prueba antes de pasar a la bornera.
  • Multímetro digital. imprescindible acá, no es opcional: sin medir la salida no tienes forma de saber si el rango quedó en 5 o en 10 V.
  • Cable USB-A a micro USB de 30 cm. para alimentar y programar la placa.

Dos ítems del montaje original no tienen equivalente directo en el catálogo local: la placa Feather RP2040 y el Metro de Adafruit que aparecen en los diagramas. No los reemplaces por "algo parecido": el ESP32 DevKit o un Arduino UNO cumplen el mismo rol de controlador I2C y el código de este tutorial corre en ambos sin cambios.

Recursos

Versión chilena inspirada en la guía original, con el ángulo de control industrial 0-10 V y componentes en stock local en MechatronicStore.