¿Tu cargador de 65 W de verdad entrega 20 V cuando conectas el notebook, o se queda en 15 V? ¿El power bank que dice 10.000 mAh cuántos miliamperios hora le pasa realmente al celular? Con un tester USB genérico ves un número que salta, pero no sabes cómo lo mide ni cuánto confiar en él. Este proyecto arma un medidor de potencia USB-C propio que se pone en línea entre la fuente y la carga, se alimenta del mismo cable que mide y muestra voltaje, corriente, potencia, carga acumulada y gráficos de los últimos 30 segundos en una pantalla IPS a color.
Al terminar vas a entender tres cosas que sirven mucho más allá de este medidor: cómo se mide corriente con una resistencia shunt en el lado alto, por qué un integrador de carga por hardware le gana a sumar lecturas en el firmware, y cómo dejar pasar una negociación USB Power Delivery sin meterte en ella.

Qué hace el medidor y qué no hace
Es un instrumento pasivo: mide entre 5 y 28 V y hasta 5 A, cubriendo desde el USB clásico de 5 V hasta los perfiles PD de 28 V (EPR). Lleva la cuenta de la carga en mAh y de la energía en Wh, guarda mínimo, máximo y promedio de la sesión, lee la temperatura interna del microcontrolador y reparte todo en once pantallas que recorres con botones.
Lo importante es lo que no hace: no dispara, no falsea ni renegocia perfiles de Power Delivery. Las líneas CC (las que usan fuente y carga para detectarse y negociar), el par de datos USB 2.0 D+/D, y el resto de las señales pasan directo de un conector al otro. El cargador y tu dispositivo conversan como si entre ellos hubiera un cable normal, y el medidor solo observa lo que acuerdan. Esa es la diferencia con los "triggers" PD, que sí piden un voltaje específico.
Otros detalles de diseño que vale la pena tener en mente desde el principio:
- Se alimenta del bus que mide: no usa batería, ni un segundo cable, ni conexión a un computador.
- La pantalla gira 180° con un toque, para que se lea bien venga el cable desde donde venga.
- Actualización por arrastrar y soltar: con un bootloader UF2, el medidor aparece como un pendrive y el firmware nuevo se copia como un archivo.
- Placa de 4 capas y 0,8 mm dentro de una carcasa impresa en 3D de dos piezas que encajan a presión.
El concepto: medir corriente con 10 miliohms
Toda la medición descansa en una idea simple. Si pones una resistencia muy pequeña en serie con VBUS, la corriente que pasa por ella produce una caída de voltaje proporcional (ley de Ohm, V = I · R). Midiendo esa caída sabes la corriente.
El valor del shunt es un compromiso. Con 10 mΩ y 5 A:
- la caída es 5 A × 0,010 Ω = 50 mV, muy poco para que la carga lo note;
- la disipación es I² · R = 25 × 0,010 = 250 mW, que una resistencia 2512 aguanta sin drama.
Si subieras el shunt a 50 mΩ ganarías resolución en corrientes bajas, pero a 5 A disiparías 1,25 W y le robarías 250 mV al dispositivo, lo que en un perfil de 5 V ya se nota. Si lo bajaras a 1 mΩ casi no perderías nada, pero la señal en corrientes de pocos miliamperios quedaría enterrada en el ruido del amplificador. Por eso 10 mΩ es el punto habitual para instrumentos USB.
El shunt va en el lado alto (entre la fuente y la carga, no en la tierra). Medir en el lado bajo es más fácil, pero deja la tierra de la carga "flotando" unos milivolts por encima de la de la fuente, y en USB eso descuadra las referencias de las señales de datos. El lado alto exige un amplificador que tolere modo común alto (hasta 28 V aquí), y eso es justo lo que trae el chip de medición.
El hardware, bloque por bloque
El esquemático se dibujó en KiCad y se divide en seis bloques: entrada USB-C, frente de medición de corriente, microcontrolador, fuente de 3,3 V, pantalla y salida USB-C.

Conectores y paso directo
J1 es un enchufe USB-C de 24 pines que va al cargador, power bank o fuente PD. J2 es un receptáculo USB-C de 16 pines donde conectas el cable hacia la carga. VBUS y tierra pasan por el circuito de medición; las líneas CC, D+/D, (y en el lado de 24 pines también los pares SuperSpeed y SBU) se enrutan hasta sus pares del otro lado. R4, el shunt de 10 mΩ, es lo único que se interpone en el camino de potencia.

Con las líneas CC hay que tener cuidado. Una fuente USB-C no entrega VBUS hasta que ve una resistencia de 5,1 kΩ a tierra en CC, que es la forma en que un "sink" anuncia que está conectado. Si el medidor tuviera esa resistencia fija, interferiría con la negociación entre fuente y carga. Por eso R1 (5,1 kΩ) va a través del pulsador SW1: solo cuando lo mantienes apretado el medidor se presenta como sink, lo que sirve para probarlo sin carga o para programarlo desde un puerto USB-C del computador. En uso normal queda desconectada.
Regulador que aguanta 28 V
El microcontrolador, la pantalla y el monitor funcionan a 3,3 V, pero VBUS puede estar en 5, 9, 15, 20 o 28 V según el perfil negociado, y pasa de uno a otro en plena operación. Un LDO típico como el AMS1117 no sirve: su entrada máxima ronda los 15 V. U2, un MCP1781-3302H de Microchip, es un regulador lineal de alto voltaje que soporta esos niveles; C10 y C11 (4,7 µF cada uno) desacoplan su entrada y salida.

Un regulador lineal convierte en calor toda la diferencia de voltaje. A 28 V de entrada y 3,3 V de salida, cada miliamperio que consume el circuito se transforma en casi 25 mW de calor en el regulador. Con unos 20 mA de consumo (pantalla con su retroiluminación incluida) son cerca de 0,5 W en un encapsulado diminuto, y por eso el diseño cuida el consumo total y regula el brillo por PWM.
El monitor de potencia PAC1811
La medición la hace un PAC1811 de Microchip: amplificador de corriente en el lado alto, monitor del voltaje del bus y un ADC de 16 bits compartido, todo en un chip que tolera hasta 42 V y consume unos 140 µA. Ese consumo bajo importa cuando mides cargas pequeñas, porque lo que gasta el propio instrumento se suma a lo que mide.

Sus entradas VSENSE+ y VSENSE, leen la caída en R4 y VBUS+ lee el voltaje del bus. Habla con el microcontrolador por I²C en la dirección 0x40, con pull ups de 4,7 kΩ (R2 y R3) en SDA y SCL y un condensador de 100 nF (C1) junto a su alimentación.
La pieza clave es su registro acumulador de carga. En vez de que el firmware lea la corriente cada tanto y la multiplique por el tiempo transcurrido, el chip integra la corriente internamente a su propia tasa de muestreo y guarda el total. El microcontrolador lo lee y lo vacía cuando puede. Si el firmware se demora medio segundo dibujando la pantalla, el chip siguió integrando y no se pierde nada. Con integración por software, cualquier pico de corriente que ocurra entre dos lecturas simplemente no existe para el contador de mAh.
El microcontrolador: ATSAMD21G18A
El cerebro es un ATSAMD21G18A, un Arm Cortex-M0+ a 48 MHz con 256 KB de flash y 32 KB de SRAM, el mismo de placas como Arduino Zero o Adafruit Feather M0. Un cristal de 32,768 kHz (Y1, con condensadores de carga de 7 pF) le da la referencia de reloj, un pull up de 10 kΩ mantiene RESET inactivo y SW4 permite reiniciarlo a mano.

¿Por qué este y no un ATmega328P o un ESP32? Por tres razones:
- Memoria para un framebuffer. La pantalla se dibuja completa fuera de ella y se envía de una vez. A 160 × 80 píxeles y 16 bits por píxel son 25.600 bytes solo para el búfer. Un ATmega328P tiene 2 KB de SRAM: imposible. El SAMD21 lo aguanta y le quedan unos 7 KB para variables y la pila. Es justo, pero alcanza.
- USB nativo. Permite el bootloader UF2: el medidor aparece como unidad de almacenamiento y se actualiza copiando un archivo, sin programador ni drivers.
- Consumo. Un ESP32 con su radio gastaría mucho más, y cada miliamperio extra se paga en calor en el regulador lineal.
Como bonus, el SAMD21 tiene un sensor de temperatura interno. No mide la temperatura del conector ni de la placa, pero sí sirve como indicador de tendencia cuando el medidor lleva rato con 5 A pasando cerca.
La programación inicial usa tres pads de prueba: TP1 (SWDIO), TP2 (SWCLK) y TP3 (RESET). Además hay dos jumpers, JP1 y JP2, que conectan el USB del microcontrolador al par D+/D, que pasa hacia la carga. Se cierran solo para programar y se sacan en uso normal: si quedan puestos, el SAMD21 queda colgado de las líneas de datos del dispositivo que estás midiendo.
Pantalla y botones
La pantalla es una IPS de 0,96 pulgadas y 160 × 80 píxeles con controlador ST7735S, conectada por SPI (datos, reloj, dato/comando y reset). El chip select va a tierra porque es el único dispositivo del bus. La retroiluminación no sale directo de un GPIO: un transistor NPN SS8050 (Q1) la conmuta con R9 limitando la corriente y R10 en la base, y como la señal admite PWM, el firmware ajusta el brillo.
Los cuatro pulsadores son activos en bajo con pull ups externos de 10 kΩ. SW1 activa la resistencia CC, SW2 y SW3 cambian de pantalla y SW4 gira la pantalla, reinicia la sesión y, si lo mantienes al encender, entra al bootloader.
La placa: por qué 4 capas en algo tan chico
Cuatro capas en una placa de este tamaño parecen exageradas, pero son la única forma de cumplir tres exigencias a la vez: un camino de VBUS ancho y de baja impedancia, una referencia analógica limpia bajo el shunt y suficientes canales para sacar las pistas de un QFN de 48 pines que vive debajo de la pantalla.
- Capa 1: el paso de alta corriente y las señales.
- Capa 2: plano de tierra sólido, sin cortes bajo el shunt y el PAC1811.
- Capa 3: el riel de 3,3 V y las señales restantes.
- Capa 4: segundo plano de tierra, unido a la capa 2 con vías alrededor de los conectores y a lo largo del camino de potencia.

El plano de tierra sin cortes bajo el shunt no es un capricho. Con 50 mV de señal a plena escala y 16 bits de resolución, un bit equivale a menos de un microvoltio en el rango más fino. Cualquier corriente de retorno que se cuele por debajo de las pistas de VSENSE genera caídas de ese orden y se lee como corriente que no existe.
Fabricación y montaje
Desde KiCad se exportan Gerber, archivos de taladro y datos de pick and place. Al pedir la placa hay dos parámetros que no son opcionales: 4 capas y 0,8 mm de espesor (no los 1,6 mm por defecto), porque la carcasa y la huella del enchufe USB-C dependen de eso. El FR4 estándar y cualquier color de máscara sirven. Si vas a soldar a mano, el acabado ENIG vale lo poco que cuesta de más: los pads planos hacen mucho más fácil ubicar el QFN y los conectores. Pedir un esténcil para la cara superior ahorra mucho tiempo con la pasta de soldar.

El repositorio trae un BOM interactivo generado desde KiCad junto a los Gerber, que te va marcando dónde va cada componente durante el montaje.

Carcasa impresa en 3D
Una placa con dos conectores USB-C y una pantalla frágil no puede andar suelta en la caja de herramientas. La carcasa tiene dos piezas: una bandeja inferior donde se apoya la placa y una tapa que encaja a presión con clips moldeados en los bordes largos. No lleva tornillos, así que se abre y se cierra las veces que quieras sin gastar insertos roscados. Tiene aberturas para ambos conectores, una ventana para la pantalla y huecos para los botones, cuyas teclas se imprimen aparte. Los STL están en el GitHub del proyecto.

El software: del bootloader al sketch
Grabar el bootloader UF2 (una sola vez)
Una placa recién armada tiene la flash vacía, así que la primera vez necesitas un programador SWD (el original usa un J-Link). Conecta TP1 a SWDIO, TP2 a SWCLK, TP3 a RESET, más tierra y 3,3 V, y corre el script ./flash_bootloader.sh del repositorio, que llama a OpenOCD así:
openocd -f interface/jlink.cfg \
-c "transport select swd" \
-f target/at91samdXX.cfg \
-c "init; reset halt; at91samd bootloader 0; \
program bootloader-usbcm-v4.0.0+.bin verify reset exit 0x00000000"
Dos partes del comando explican la mayoría de los "no me funciona":
at91samd bootloader 0borra el fusible BOOTPROT. En un SAMD21 de fábrica, ese fusible protege contra escritura justo la zona baja de la flash donde tiene que quedar el bootloader. Si te lo saltas, la grabación parece exitosa pero no escribe nada.0x00000000pone el binario al inicio de la flash, que es donde el Cortex-M0+ busca su tabla de vectores al arrancar.
Después, desconecta el programador, cierra JP1 y JP2 y conecta la placa al computador manteniendo apretado SW1 (para que un puerto USB-C del computador vea la resistencia CC y encienda VBUS) hasta que termine. Debe aparecer como una unidad de almacenamiento. Desde ahí no vuelves a necesitar el programador SWD.
Cargar el firmware desde el Arduino IDE
Con el bootloader UF2 la placa se comporta como cualquier SAMD21 de Adafruit:
- Cierra JP1 y JP2. Si usas un puerto USB-C del computador, mantén SW1 apretado al conectar.
- En Archivo → Preferencias agrega esta URL en "Additional Board Manager URLs":
https://adafruit.github.io/arduino-board-index/package_adafruit_index.json - En el gestor de placas instala Arduino SAMD Boards y después Adafruit SAMD Boards, y elige Adafruit Feather M0. Ese perfil calza con el microcontrolador y el bootloader de esta placa, así que no hace falta una variante propia.
- Instala desde el gestor de librerías: Adafruit GFX Library, Adafruit ST7735 and ST7789 Library, PAC1xxx (de Jobit Joseph, el driver del PAC1811) y TemperatureZero (de Electronic Cats, para la temperatura interna del SAMD21).
- Abre el sketch, elige el puerto y sube. Si el reinicio automático falla, presiona dos veces reset para forzar la unidad del bootloader y vuelve a subir.
- Saca JP1 y JP2 antes de usar el medidor en línea.

El último paso es el que más se olvida. Dejar los jumpers puestos no altera ninguna medición, y justamente por eso es traicionero: el medidor funciona perfecto mientras el dispositivo conectado tiene problemas de comunicación USB que nadie asocia al medidor.
Cómo está organizado el firmware
El sketch tiene unas 1.500 líneas repartidas en espacios de nombres (Display, PowerSensor, Screens y el manejo de botones), y el loop() principal casi solo los llama. Cinco mecanismos explican todo.
1. Las pantallas son una tabla. Cada pantalla es una función con la misma firma que recibe las lecturas, su índice y el total de páginas, y se dibuja sola. Todas viven en un arreglo:
static ScreenDef SCREENS[] = {
{"V / I / P", draw_VIP, true},
{"V / I / mAh", draw_VImAh, true},
{"V / I / Wh", draw_VIWh, true},
{"Temp / Session", draw_TempSession, true},
{"Min (V/I/P)", draw_Min, true},
{"Max (V/I/P)", draw_Max, true},
{"Avg (V/I/P)", draw_Avg, true},
{"Voltage graph", draw_GraphV, true},
{"Current graph", draw_GraphI, true},
{"Power graph", draw_GraphP, true},
{"Combined V+I graph", draw_GraphCombined, true},
};
El orden de navegación sigue el orden del arreglo. Poner false en una entrada la saca del ciclo sin borrar su código, y los puntos indicadores de página cuentan solo las habilitadas. Si quieres que tus tres pantallas favoritas salgan primero, mueves tres líneas y listo.
2. La carga se integra en el chip. El sensor se lee cada 100 ms; cada lectura actualiza mínimo, máximo y promedio, y el historial de los gráficos avanza cada un segundo. El corazón del instrumento son estas tres líneas:
pac.refresh();
r.mAh += pac.charge_mAh();
r.Wh = (r.mAh / 1000.0f) * r.vAvg;
refresh() congela el acumulador del PAC1811, lo vacía y devuelve la carga acumulada desde la llamada anterior. Sumando esos retornos tienes un total de mAh sin huecos.
La energía (Wh) se deriva en vez de acumularse, porque el PAC1811 acumula carga o potencia, pero no ambas a la vez. El firmware elige carga, porque los mAh son lo que la mayoría busca en un medidor USB, y calcula Wh multiplicando por el voltaje promedio. ¿Cuánto error mete eso? En USB-C, el voltaje queda fijo en el nivel negociado y varía muy poco con la carga, así que el error es mínimo. Sería mayor en una fuente cuyo voltaje cae justo cuando sube la corriente (una batería sin regular, por ejemplo): ahí el promedio de V·I no es igual al promedio de V multiplicado por el promedio de I. Para USB-C la aproximación es buena; para medir una batería directa, conviene acumular potencia.
La temperatura interna se lee solo una vez por segundo. La conversión del SAMD21 promedia 64 muestras y bloquea unos 25 ms: hacerla cada 100 ms se comería un cuarto del tiempo del loop por un dato que cambia lento.
3. Doble búfer en RAM. Borrar la pantalla y redibujar produce un parpadeo visible dos veces por segundo. En cambio, cada cuadro se compone en un lienzo fuera de pantalla:
static GFXcanvas16 canvas(TFT_W, TFT_H); // 160 * 80 * 2 = 25,600 bytes
Ese lienzo usa casi toda la SRAM. Si la asignación fallara, GFXcanvas16 simplemente no dibuja: el peor caso es una pantalla en blanco, no memoria corrupta.
4. Enviar el cuadro por franjas. La llamada obvia, drawRGBBitmap(), escribe píxel por píxel y reenvía la ventana de direcciones cada vez; por SPI por software eso tarda del orden de un segundo por cuadro, y los botones parecen muertos. La solución fija la ventana una vez y manda el búfer contiguo, en franjas de diez filas con una lectura de botones entre cada una:
const int STRIP_ROWS = 10;
for (int y = 0; y < TFT_H; y += STRIP_ROWS) {
int h = (TFT_H - y) < STRIP_ROWS ? (TFT_H - y) : STRIP_ROWS;
tft.startWrite();
tft.setAddrWindow(0, y, TFT_W, h);
tft.writePixels(buf + (uint32_t)y * TFT_W, (uint32_t)TFT_W * h);
tft.endWrite();
handleButtons(); // safe: only sets state flags, never draws
}
Así el hueco entre dos lecturas de botones baja a unos pocos milisegundos. El manejador de botones solo levanta banderas y nunca dibuja, para no volver a entrar al renderizador a mitad de un cuadro.
5. Fuentes que se ajustan solas. Escalar una fuente de 5 × 7 da dígitos de bloques difíciles de leer de un vistazo. El firmware lleva cuatro tamaños de FreeSansBold y elige el más grande que cabe en el espacio disponible:
static const GFXfont *BIG_FONTS[] = {
&FreeSansBold24pt7b, &FreeSansBold18pt7b,
&FreeSansBold12pt7b, &FreeSansBold9pt7b};
Por eso la pantalla de temperatura y sesión, que tiene solo dos filas, muestra dígitos más grandes que las de tres filas. Los valores tienen ancho fijo (el voltaje siempre con dos enteros y tres decimales, la corriente con uno y cuatro) para que los dígitos no bailen. Como FreeSans es solo ASCII, el símbolo de grado se dibuja con dos círculos pequeños.
Un detalle que te ahorra una tarde: los tres botones están en PA13, PA14 y PA15, y el perfil Feather M0 limita su tabla de pines a 26, así que pinMode(), digitalRead() y attachInterrupt() no reconocen esos pines aunque el chip los tenga. El firmware los lee directo desde los registros del puerto, con los pull ups internos apagados (ya hay externos de 10 kΩ), un antirrebote de 25 ms y un detector de pulsación larga de 1,5 s que distingue un toque de una pulsación mantenida.
Pruebas: las once pantallas
Al encender aparece una pantalla de arranque y luego la primera página: voltaje, corriente y potencia. Las siguientes cambian la tercera fila por la carga acumulada en mAh o la energía en Wh.

Luego vienen temperatura y tiempo de sesión, y los mínimos, máximos y promedios de voltaje, corriente y potencia.

Las últimas cuatro son gráficos de 30 segundos: voltaje, corriente, potencia y voltaje con corriente combinados.

Una forma rápida de validar que mide bien: conecta una carga conocida (una resistencia de potencia o una carga electrónica ajustada a 1 A) con un multímetro en serie, y compara. Después prueba un cargador PD con un notebook y mira en el gráfico de voltaje el salto de 5 V al perfil negociado: si lo ves, confirmas que el medidor deja pasar la negociación sin intervenir.
Si algo falla
- La grabación SWD "funciona" pero no hay unidad UF2: faltó
at91samd bootloader 0, o JP1/JP2 están abiertos. - Al conectar a un puerto USB-C del computador no enciende: no estás manteniendo SW1, así que el puerto nunca ve la resistencia de 5,1 kΩ y no entrega VBUS.
- El dispositivo medido no se conecta por datos: quedaron puestos JP1 y JP2.
- Los botones no responden: si modificaste el código, revisa que no estés usando
digitalRead()en PA13 a PA15. - Corriente con offset en vacío: revisa la soldadura del shunt; una soldadura fría en una resistencia de 10 mΩ agrega resistencia del mismo orden y descalibra la lectura.
Variantes y mejoras
- Versión en protoboard, sin fabricar placa: un módulo INA219 o INA226 con su propio shunt, un Arduino o un ESP32 y una pantalla ST7735 de 0,96" reproducen la medición de voltaje, corriente y potencia. No tendrás el integrador por hardware (el INA226 no acumula carga), así que integra en el firmware con un intervalo fijo y corto, y usa un breakout USB-C macho/hembra para intercalarlo.
- Registro de datos: agrega una tarjeta microSD por SPI (ojo con la RAM: el framebuffer ya ocupa casi todo, así que en el SAMD21 conviene escribir en bloques chicos) para guardar curvas completas de carga de un power bank y comparar marcas.
- Envío inalámbrico: con un ESP32-C3 en vez del SAMD21 puedes mandar las lecturas por WiFi a Home Assistant o a una planilla. Tendrás que medir el consumo extra y revisar el calor del regulador a 20 V o más.
- Acumular energía en vez de carga: si lo vas a usar con baterías sin regular, configura el PAC1811 para acumular potencia y deriva los mAh, que es el caso donde el método original pierde precisión.
Personalización para Chile
La placa original usa componentes SMD muy específicos (el PAC1811, el MCP1781, el ATSAMD21 en QFN y conectores USB-C de montaje superficial) que se compran a distribuidores como DigiKey o Mouser, y la placa de 4 capas se manda a fabricar. Para la versión en protoboard que proponemos en "Variantes y mejoras", en MechatronicStore puedes encontrar las piezas equivalentes:
- Pantalla TFT ST7735 de 0,96": el mismo controlador que usa el medidor original, así que el código de dibujo con Adafruit GFX y la librería ST7735 se aprovecha casi entero.
- Módulo medidor de corriente INA219 o INA226: reemplaza al PAC1811 con shunt incluido, por I²C. El INA226 llega a 36 V, suficiente para perfiles PD de hasta 28 V; el INA219 llega a 26 V, así que no sirve para EPR de 28 V.
- Placa con SAMD21 (tipo Arduino Zero) o un Arduino Nano / ESP32: el SAMD21 te deja usar el sketch casi igual; con un ESP32 tienes memoria de sobra para el framebuffer.
- Breakout o conectores USB-C para intercalar el medidor en el cable.
- Protoboard y jumpers para el montaje de prueba.
Si en el tutorial original usan una placa propia con ATSAMD21G18A, una placa de desarrollo con el mismo chip cumple la misma función a nivel de firmware (el original de hecho se programa con el perfil Adafruit Feather M0).
Recursos
- Tutorial original (inglés): Build Your Own USB-C Power Meter, de Jobit Joseph en Circuit Digest. Este artículo está basado en él.
- Repositorio GitHub: Circuit Digest/USB-C-Power Meter con el sketch, el script del bootloader y los STL de la carcasa.
- Archivos de fabricación: Hardware en KiCad y Gerber en ZIP
- Paquete de placas Adafruit:
https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
Versión chilena con componentes en stock local en MechatronicStore.




